Java线程
一、程序,进程,线程
程序是一段静态的代码,它是应用程序执行的蓝本。
什么是进程?一个进程可以有多线程
进程是指一种正在运行的程序,有自己的地址空间。
作为蓝本的程序可以被多次加载到系统的不同内存区域分别执行,形成不同的进程。
基于进程的特点是允许计算机同时运行两个或更多的程序。
什么是线程?
二、实现多线程的方法
1.Thread class
子类继承Thread具备多线程能力
启动线程,子类对象.start();
package Kuang;
/**
* description://创建线程方法一:继承Thread类
* author:zcx
* lenovo
* 时间:2022.02.08.18.44
*/
//创建线程方法一:继承Thread类
public class Demo1 extends Thread {
@Override
public void run(){
//run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("run()"+i);
}
}
public static void main(String[] args) {
//main线程,主线程
//创建一个线程对象
var t=new Demo1();
t.run();
t.start();
for (int i = 0; i < 20; i++) {
System.out.println("main"+i);
}
}
}
2.Runnable接口
实现Runnable接口具备多线程能力
启动线程:传入目标对象+Thread对象.start();
package Kuang;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/**
* description:下载图片,实现Runnable接口
* author:zcx
* lenovo
* 时间:2022.02.08.19.06
*/
public class Demo2 implements Runnable {
private String url;//网络图片地址
private String name;//保存的文件名
public Demo2(String url,String name){
this.url=url;
this.name=name;
}
@Override
public void run() {
WebDownloader wl= new WebDownloader();
wl.downloader(url,name);
System.out.println("下载文件名为:"+name);
}
//下载器
static class WebDownloader {
//下载方法
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}
}
}
public static void main(String[] args) {
Demo2 t1=new Demo2("https://tenfei01.cfp.cn/creative/vcg/800/new/VCG211363439413.jpg","1.jpg");
new Thread(t1).start();
}
}
package Kuang;
import java.util.Locale;
/**
* description:模拟卖票
* author:zcx
* lenovo
* 时间:2022.02.08.19.36
*/
public class Demo4 implements Runnable{
//票数
final Object lock=new Object();
private int ticketNum=10;
@Override
public void run() {
String tn=Thread.currentThread().getName();
while (true) {
synchronized (lock) {
if (ticketNum <= 0) {
// System.out.println(tn+"票已售完");
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(tn + "--->拿到了第" + ticketNum-- + "票");
}
}
}
public static void main(String[] args) {
Demo4 t=new Demo4();
new Thread(t,"老师").start();
new Thread(t,"小明").start();
new Thread(t,"黄牛党").start();
}
}
3.Callable
/*
* Copyright (c) 2006, 2021, webrx.cn All rights reserved.
*/
package cn.webrx;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* <p>Powered by webrx On 2021-12-24 10:48:59
* @author webrx [webrx@126.com]
* @since 17
*/
public class T3 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("call()");
return 300;
}
public static void main(String[] args) {
T3 t3 = new T3();
// 执行 Callable 方式,需要 FutureTask 实现类的支持
// FutureTask 实现类用于接收运算结果, FutureTask 是 Future 接口的实现类
FutureTask<Integer> result = new FutureTask<>(t3);
new Thread(result).start();
// 接收线程运算后的结果
try {
// 只有当 Thread 线程执行完成后,才会打印结果;
// 因此, FutureTask 也可用于闭锁
System.out.println(result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
三、多线程状态和常用方法
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。
线程的优先级用数字表示,范围从1~10.
Thread.MIN_PRIORITY= 1;
Thread.MAX_PRIORITY= 10;
Thread.NORM_PRIORITY= 5;
使用以下方式改变或获取优先级
getPriority() . setPriority(int xxx)
package Kuang;
/**
* description:线程优先级设置
* author:zcx
* lenovo
* 时间:2022.02.09.19.19
*/
public class Demo11 {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
var my=new Myp();
var t1=new Thread(my);
var t2=new Thread(my);
var t3=new Thread(my);
var t4=new Thread(my);
var t5=new Thread(my);
//先设置优先级
t1.start();
t2.setPriority(1);
t3.setPriority(4);
t4.setPriority(8);
t5.setPriority(Thread.MAX_PRIORITY);
t2.start();
t3.start();
t4.start();
t5.start();
}
}
class Myp implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
}
package cn.zcx;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* description:定时关机
* author:zcx
* lenovo
* 时间:2022.02.07.19.30
*/
public class T10 {
public static void main(String[] args) {
//多长时间后关机5s
new Thread(()->{
long t1=System.currentTimeMillis();
while (true){
long ok=System.currentTimeMillis()-t1;
if(ok>=5*1000)
break;
}
System.out.println("系统正在关机5....");
}).start();
//
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("系统正在关机10s");
}).start();
//指定时间关机2021-2-7 21:30:00
new Thread(()->{
String s="2022-2-7 21:30:59";
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
long end=0;
try {
end=sdf.parse(s).getTime();
} catch (ParseException e) {
e.printStackTrace();
}
try {
TimeUnit.MILLISECONDS.sleep(end-System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("%s - 系统正在关机%n",s);
}).start();
}
}
四、多线程工具类
Timer类的常用其他方法:
cancel()
终止此计时器,丢弃所有当前已安排的任务。
purge()
从此计时器的任务队列中移除所有已取消的任务。
schedule(TimerTask task, Date time)
安排在指定的时间执行指定的任务。
TimerTask类的常用其他方法:
cancel()
取消此计时器任务。
run()
此计时器任务要执行的操作。
scheduledExecutionTime()
返回此任务最近行的已安排执行时间。
1.线程停止wait()
package cn.zcx;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* description:
* author:zcx
* lenovo
* 时间:2022.02.07.14.16
*/
public class T16 {
Object lock = new Object();
public static void main(String[] args) {
var ex = new T16();
}
void start(){
String t = Thread.currentThread().getName();
System.out.printf("%s线程启动%n", t);
synchronized (lock){
lock.notifyAll();
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
void add() {
String t = Thread.currentThread().getName();
System.out.printf("%s线程启动%n", t);
synchronized (lock) {
try {
//lock.
lock.wait();
Thread.sleep(2000);
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.printf("");
}
}
2.,.线程休眠sleep()
package Kuang;
import java.util.Random;
/**
* description:
* author:zcx
* lenovo
* 时间:2022.02.08.20.08
*/
public class Demo5 implements Runnable {
private static String winner;
@Override
public void run() {
Random r=new Random();
String tn=Thread.currentThread().getName();
for (int i = 0; i <= 100; i++) {
boolean flag=gameOver(i);
if(tn.equals("兔子")&&i%10==0){
try {
Thread.sleep(r.nextInt(2));//随机休眠
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(flag){
break;
}
System.out.println(tn + "--->跑了"+i+"步");
}
}
private boolean gameOver(int steps) {
if (winner != null) {
return true;
} else if (steps >= 100) {
winner = Thread.currentThread().getName();
System.out.println("winner is" + winner);
return true;
}
return false;
}
public static void main(String[] args) {
var t=new Demo5();
new Thread(t,"兔子").start();
new Thread(t,"乌龟").start();
}
}
3.线程礼让yield
package Kuang;
/**
* description:
* author:zcx
* lenovo
* 时间:2022.02.09.09.16
*/
public class Demo8 {
public static void main(String[] args) {
var t=new Myyeild();
new Thread(t,"A").start();
new Thread(t,"B").start();
}
}
class Myyeild implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "a");
Thread.yield();
System.out.println(Thread.currentThread().getName() + "b");
}
}
4.线程强行执行join
package Kuang;
/**
* description:join()插队
* author:zcx
* lenovo
* 时间:2022.02.09.18.48
*/
public class Demo9 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("我是大哥"+i);
}
}
public static void main(String[] args) {
//启动线程
var t=new Demo9();
var tt=new Thread(t);
tt.start();
//主线程
for (int i = 0; i < 500; i++) {
if(i==100){
try {
tt.join();//插队
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("main"+i);
}
}
}
package Job;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* description:原子操作类的多个方法调用并不构成原子性
* author:zcx
* lenovo
* 时间:2022.02.08.14.27
*/
public class Job9 {
AtomicInteger count = new AtomicInteger(0);
synchronized void m() {
for (int i = 0; i < 10000; i++) {
if (count.get() < 100 && count.get() >= 0) { //如果未加锁,之间还会有其他线程插进来
count.incrementAndGet();
}
}
}
public static void main(String[] args) {
var t=new Job9();
for (int i = 0; i < 10; i++) {
new Thread(t::m,"T"+i).start();
}
try {
TimeUnit.SECONDS.sleep(1);
System.out.println(t.count.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package cn.zcx;
import java.util.concurrent.TimeUnit;
/**
* description:证明volatile不是原子操作
* author:zcx
* lenovo
* 时间:2022.02.08.09.44
*/
public class T28 {
/*volatile*/ boolean running = true; //对比一下有无volatile的情况下,整个程序运行结果的区别
void m() {
System.out.println("m start");
while(running) {
/*
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
System.out.println("m end!");
}
public static void main(String[] args) {
T28 t = new T28();
new Thread(t::m, "t1").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.running = false;
}
}
7.静态代理
package Kuang;
/**
* description:静态代理,结婚案例
* author:zcx
* lenovo
* 时间:2022.02.08.20.40
*/
public class Demo6 {
public static void main(String[] args) {
new Thread(()->{
System.out.println("我爱你");
}).start();
new WeddingCompang(new You()).HappyMarry();
var wed=new WeddingCompang(new You());
wed.HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
//真实角色
class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("结婚");
}
}
//代理角色,帮助你结婚
class WeddingCompang implements Marry{
private Marry target;
public WeddingCompang(Marry target){
this.target=target;
}
@Override
public void HappyMarry() {
before();
this.target.HappyMarry();
after();
}
private void before(){
System.out.println("结婚之前,布置现场");
}
private void after(){
System.out.println("结婚之后,收尾款");
}
}
五、线程池
在Java 5之后,并发编程引入了一堆新的启动、调度和管理线程的API。Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。因此,在Java 5之后,通过Executor来启动线程比使用Thread的start方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免this逃逸问题——如果我们在构造器中启动一个线程,因为另一个任务可能会在构造器结束之前开始执行,此时可能会访问到初始化了一半的对象用Executor在构造器中。Eexecutor作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务的线程相当于消费者,并用Runnable来表示任务,Executor的实现还提供了对生命周期的支持,以及统计信息收集,应用程序管理机制和性能监视等机制。
Executor框架包括:线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。
-
线程池提供了一个线程队列,队列中保存着所有等待状态的线程;
-
-
线程池的体系结构
-
java.util.concurrent.Executor
: 负责线程的使用和调度的根接口; -
ExecutorService
: 子接口,线程池的主要接口; -
ThreadPoolExecutor
: 线程池的实现类; -
ScheduledExecutorService
: 子接口,负责线程的调度; -
ScheduledThreadPoolExecutor
: 继承了线程池的实现类,实现了负责线程调度的子接口;
-
-
工具类:
Executors
-
ExecutorService newFixedThreadPool()
: 创建固定大小的线程池; -
ExecutorService newCachedThreadPool()
: 缓存线程池,线程池中线程的数量不固定,可以根据需求自动更改数量; -
ExecutorService newSingleThreadExecutor()
: 创建单个线程池, 线程池中只有一个线程; -
ScheduledExecutorService newScheduledThreadPool()
: 创建固定大小的线程,可以延时或定时的执行任务;
-
六、
java.util.concurrent.ForkJoinPool由Java大师Doug Lea主持编写,它可以将一个大的任务拆分成多个子任务进行并行处理,最后将子任务结果合并成最后的计算结果,并进行输出。本文中对Fork/Join框架的讲解,基于JDK1.8+中的Fork/Join框架实现,参考的Fork/Join框架主要源代码也基于JDK1.8+。
这几篇文章将试图解释Fork/Join框架的知识点,以便对自己、对各位读者在并发程序的设计思路上进行一些启发。文章将首先讲解Fork/Join框架的基本使用,以及其中需要注意的使用要点;接着使用Fork/Join框架解决一些实际问题;最后再讲解Fork/Join框架的工作原理。
package Job;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* description:用五个线程实现,求123456789 之间放+-和100的表达式,如果一个线程求出结果,立即告诉其它停止。
* author:zcx
* lenovo
* 时间:2022.02.07.21.20
*/
public class J5 {
volatile List<String> list = new ArrayList<>();
void op() {
System.out.printf("%s 启动计算中 %n", Thread.currentThread().getName());
String[] o = new String[]{"", "+", "-"};
Random rand = new Random();
StringBuffer str = new StringBuffer("1");
Pattern p = Pattern.compile("(\\d+|-\\d+)");
while (list.size()!=11) {
for (int i = 2; i < 10; i++)
str.append(String.format("%s%d", o[rand.nextInt(o.length)], i));
String s = str.toString();
Matcher m = p.matcher(s);
List<Integer> ln = new ArrayList<>();
while (m.find()) {
ln.add(Integer.parseInt(m.group()));
}
int sum = ln.stream().reduce(0, Integer::sum);
if (sum == 100 && !list.contains(s)) {
list.add(s);
System.out.printf("[%s]:%s = 100%n", Thread.currentThread().getName(), s);
} else {
str.delete(1, str.length());
}
}
System.out.printf("%s 结束 %n", Thread.currentThread().getName());
}
public static void main(String[] args) {
var t = new J5();
for (int i = 0; i < 15; i++)
new Thread(t::op, "T" + i).start();
}
}