线程
约 6250 字大约 21 分钟
2026-04-04
并发 :指两个或者多个事件在同一个时间段 内发生
并行 :之两个或者多个事件在同一时刻 发生

程序 :可执行的文件
进程 :一个内存中运行的应用程序,每个进程都有自己独立的内存空间,一个应用程序可以运行多个进程
进程也是程序的一次执行过程,是系统运行的基本单位,系统运行程序即是一个进程从创建到消亡的过程
线程 :是进程中的一个最小执行单元,负责当前进程的执行,一个进程中至少有一个线程,一个进程中可以有多个线程,当一个程序有多个线程执行时,称为这个程序为多线程程序
简而言之:一个程序运行后至少有一个进程,一个进程可以包含多个线程
线程的调度
分时调度 :所有的线程轮流使用CPU的使用权,平均分配每个线程的占用CPU的时间
抢占式调度 :优先让优先级比较高的线程来使用CPU,如果线程的优先级相同,那么会随机选择一个,java使用的就是抢占式调度
创建线程
继承Thread类
Thread类是描述线程的类,我们想要实现多线程,就必须要继承它
实现步骤
创建一个类继承
Thread重写
Run方法,设置线程任务创建一个
Thread子类对象调用
start()开启多线程
//子线程
public class MyThread extends Thread{
@Override
public void run() {
System.out.println(111);
}
}
public class Demo {
//主线程
public static void main(String[] args) {
Thread thread=new MyThread();
thread.start();
for (int i = 0; i < 50; i++) {
System.out.println(222);
}
}
}常用方法
public Thread() 分配一个新的 Thread对象public final String getName() 返回此线程的名称 。 public final void setName(String name) 将此线程的名称更改 为等于参数namepublic static void sleep(long millis) 使当前正在执行的线程停留 (暂停执行)指定的毫秒数 public static Thread currentThread() 返回对当前正在执行的线程对象 的引用 。
public class MyThread extends Thread{
@Override
public void run() {
try {
Thread.currentThread().sleep(2000);
Thread.currentThread().setName("子线程1");
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class DemoThread {
public static void main(String[] args) {
MyThread t=new MyThread();
t.start();
Thread.currentThread().setName("主线程main");
System.out.println(Thread.currentThread().getName());
}
}
实现Runnable接口
Runnable接口:应该由那些打算通过某一线程执行其实例的类来实现。实现了接口后需要重写run方法
实现步骤
创建一个类,实现
Runnable接口在实现类中重写
run方法,并设置线程任务创建实现类对象
创建Thread类对象,构造方法中传递实现类对象调用Thread类中的
start(),开启多线程
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
public class DemoRunnable {
public static void main(String[] args) {
MyRunnable mr=new MyRunnable();
Thread thread=new Thread(mr);
thread.start();
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
实现Runnable接口的优点
- 避免了单继承的局限性;
1. 一个类只能继承一个类,如果这个类继承Thread就不能再继承其他的类
2. 实现了Runnable接口,可还可以继承其他的类和接口
- 增强程序的扩展性,降低程序的耦合度
1. 实现接口,把设置线程任务和开启线程任务进行了分离
2. 实现类中,重写了run方法:用来设置线程任务
3. 创建Thread类对象,调用start方法:开启多线程
使用匿名内部类创建多线程
public class InnerClassRunnable {
public static void main(String[] args) {
Thread t=new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getId() + ":" + i);
}
}
});
t.start();
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getId() + ":" + i);
}
}
}实现 Callable接口
创建线程池
下面线程池讲解
线程安全
如果由多个线程在同时运行,而且这些线程可能会同时运行这段代码,那么程序每次运行结果和单线程每次运行的结果是一样的,且其他的变量的值和预期是一样的,这就是线程安全的
出现线程安全的原因
使用了多线程访问同一个资源,而且多个线程对同一个资源进行了操作
解决线程安全
需要使用同步代码块 (synchronized)
(1)同步代码块
格式
synchronized(锁对象){
可能发生安全问题的代码
}注意
通过代码中的锁对象,可以使用任意的对象
但是必须要保证多个线程使用的锁对象是同一个
锁对象的作用:把同步代码块加锁,只让一个线程在同步代码块中执行
public class Demo1 {
static int ticketCount=50;
static Object obj=new Object();
public static void main(String[] args) {
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
while (true){
synchronized (obj){
if (Demo1.ticketCount<=0){
System.out.println("窗口1票已售完!");
return;
}else{
System.out.println("窗口1正在卖第"+Demo1.ticketCount--+"张票");
}
}
}
}
});
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
while (true){
synchronized (obj){
if (Demo1.ticketCount<=0){
System.out.println("窗口2票已售完!");
return;
}else {
System.out.println("窗口2正在卖第"+Demo1.ticketCount--+"张票");
}
}
}
}
});
Thread t3=new Thread(new Runnable() {
@Override
public void run() {
while (true){
synchronized (obj){
if (Demo1.ticketCount<=0){
System.out.println("窗口3票已售完!");
return;
}else{
System.out.println("窗口3正在卖第"+Demo1.ticketCount--+"张票");
}
}
}
}
});
t1.start();
t2.start();
t3.start();
}
}
(2)同步方法
修饰词 synchronized 返回值 方法名称(参数){}此时锁住的是对象本身,调用者自己本身
(3)锁机制
public interface Lock: Lock实现提供比使用synchronized方法个语句可以获得更广泛的锁定操作 ReentrantLock Lock锁也称为同步锁 : void lock() 上锁void unlock 释放锁
ReentrantLock lock=new ReentrantLock();
lock.lock();
lock.unlock();public class Demo {
static int sum=0;
static Object obj=new Object();
public static void main(String[] args) {
ReentrantLock lock=new ReentrantLock();
lock.lock();
new Thread(()->{Demo.sum++;}).start();
new Thread(()->{Demo.sum++;}).start();
new Thread(()->{Demo.sum--;}).start();
new Thread(()->{Demo.sum--;}).start();
lock.unlock();
new Thread(new MyThread1()).start();
}
}锁池和等待池
锁池
假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权 ,但是该对象的锁目前正被线程A拥有 ,所以这些线程就进入了该对象的锁池中。
等待池
假设一个线程A调用 了某个对象的wait()方法 ,线程A就会释放该对象的锁 后,进入到了该对象的等待池中
https://blog.csdn.net/weixin_42504145/article/details/85329386
线程生命周期

当程序使用 new 关键字 创建了一个线程之后,该线程就处于新建状态,此时仅由 JVM 为其分配 内存,并初始化其成员变量的值
当线程对象调用了 start()方法 之后,该线程处于就绪状态。Java 虚拟机会为其创建方法调用栈和 程序计数器,等待调度运行。
如果处于就绪状态的线程获得了 CPU,开始执行 run()方法的线程执行体 ,则该线程处于运行状态。
阻塞状态是指线程因为某种原因放弃了 cpu 使用权 ,也即让出了 cpu timeslice,暂时停止运行。 直到线程进入可运行(runnable)状态,才有机会再次获得 cpu timeslice 转到运行(running)状态。

- 线程会以三种方式结束,结束后就是死亡状态。

public final native void notify()
线程被唤醒
public final native void wait()
线程调用该方法后线程会进入
等待状态
public class WaittingDemo {
public static void main(String[] args) {
Object obj=new Object();
new Thread(new Runnable() {
@Override
public void run() {
while (true){
synchronized (obj){
System.out.println("老板来碗热干面");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("开吃");
System.out.println("============");
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj){
System.out.println("面做好了,准备开吃");
obj.notify();
}
}
}
}).start();
}
}sleep 与 wait 区别
对于
sleep()方法,我们首先要知道该方法是属于 Thread 类**中的。而 ** wait() 方法,则是属于 Object 类中的sleep()方法导致了程序暂停执行指定的时间,让出 cpu 该其他线程,但是他的监控状态依然保持,当指定的时间到了又会自动恢复运行状态。
在调用 sleep()方法的过程中,线程不会释放对象锁 。
而当调用 wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池 ,只有针对此对象调用 notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
start 与 run 区别
start()方法来启动线程 ,真正实现了多线程运行。这时无需等待 run 方法体代码执行完毕 ,可以直接继续执行下面的代码。
通过调用 Thread 类的 start()方法来启动一个线程, 这时此线程是处于就绪状态 , 并没有运行。
方法 run()称为线程体,它包含了要执行的这个线程的内容,线程就进入了运行状态 ,开始运行 run 函数当中的代码 。 Run 方法运行结束, 此线程终止。然后 CPU 再调度其它线程。
线程通信
/**
* @Description 面条
* @Classname Noddle
* @Date 2021/8/13 9:21
* @Created by 折腾的小飞
*/
public class Noddle {
String name;
String burdening;
//状态:true有,false没有
boolean flag = false;
}
/**
* @Description 早餐的生产者、
* 生产早餐
* noodle
* 判断是否已经有面条
* 如果有则wait一段时间,表示卖面条
* 如果没有则开始制作
* <p>
* 早餐店和面条之间的关系 互斥
* @Classname Sell
* @Date 2021/8/13 9:17
* @Created by 折腾的小飞
*/
public class Sell extends Thread {
private Noddle noddle;
//通过构造方法设置面条值
public Sell(Noddle noddle) {
this.noddle = noddle;
}
@Override
public void run() {
//定义一个变量
int count = 0;
//生产面条
while (true) {
synchronized (noddle) {
//对面条进行判断
if (noddle.flag == true) {
// 有就停止制作面,进行卖面条操作;
try {
noddle.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 被唤醒后,相当于开始生产面条
if (count % 2 == 0) {
noddle.name="热干面";
noddle.burdening="花生酱+香菜";
}else{
noddle.name="炸酱面";
noddle.burdening="葱";
}
count++;
System.out.println("早餐店正在生产:"+noddle.name+",加的配料是:"+noddle.burdening);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
noddle.flag=true;
noddle.notify();
System.out.println("我的"+noddle.name+"已经做好了,给你加的配料是:"+noddle.burdening+",可以来吃了!");
}
}
}
}
/**
* @Description 买方 买早餐
* 买面条的
* 需要判断有没有面条
* 状态:false 进入等待期wait()
* true 开始吃面条
* 吃完了,把面条状态改为false
* 再叫sell做面条
* @Classname Buyer
* @Date 2021/8/13 9:27
* @Created by 折腾的小飞
*/
public class Buyer extends Thread {
private Noddle noddle;
public Buyer(Noddle noddle) {
this.noddle = noddle;
}
@Override
public void run() {
//让客户一直来吃面条
while (true) {
synchronized (noddle) {
if (noddle.flag == false) {
try {
noddle.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//唤醒后开始吃面条
System.out.println("顾客正在享用早餐:【" + noddle.name + "】,加的配料是:【" + noddle.burdening + "】");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//吃完
noddle.flag = false;
//叫早餐店生产面条
noddle.notify();
System.out.println("老板我的面吃完了,再来一碗");
System.out.println("========================");
}
}
}
}
/**
* @Description 开启多线程任务
* @Classname Demo
* @Date 2021/8/13 9:15
* @Created by 折腾的小飞
*/
public class Demo {
public static void main(String[] args) {
Noddle noddle=new Noddle();
new Sell(noddle).start();
new Buyer(noddle).start();
}
}线程池
就是一个容纳多个线程的容器,其中的线程可以反复地利用,省去了我们频繁创建线程对象的操作,无需反复创建线程而消耗过多的资源;
为了节省资源的一个消耗

Java 里面线程池的顶级接口是 Executor,但是严格意义上讲 Executor 并不是一个线程池,而 只是一个执行线程的工具。
真正的线程池接口是
ExecutorService
Executors
JDK1.5以后提供了一个线程池工厂类:public class Executors
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
在任意点,在大多数 nThreads 线程会处于处理任务的活动状态 。 如果在所有线程处于活动状态时提交附加任务 , 则在有可用线程之前,附加任务将在队列中等待 。
如果在关闭前的执行期间由于失败而导致任何 线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之 前,池中的线程将一直存在。
newCachedThreadPool
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) 创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们
**调用 ** execute 将重用以前构造 的线程(如果线程可用)。
如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并 从缓存中移除那些已有 60 秒钟未被使用的线程。
对于执行 很多短期异步任务 的程序而言,这些线程池通常可提高程序性能。 因此,长时间保持空闲的线程池不会使用任何资源。
newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 创建一个线程池,可以调度命令在给定的延迟后运行,或者定期执行。
ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
提交在给定延迟后启用的一次性任务。
参数
command- 要执行的任务delay- 从现在起延迟执行的时间unit- 延迟参数的时间单位
结果
表示任务未完成的ScheduledFuture,其完成后 get()方法将返回 null
异常RejectedExecutionException - 如果无法安排执行任务 NullPointerException - 如果命令或单位为空
ScheduledFuture]<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) 提交定期操作,该操作在给定的初始延迟后首先启用,随后在给定的时间段内启用; 也就是说,执行将在initialDelay之后开始,然后是initialDelay + period ,然后是initialDelay + 2 * period ,依此类推。【固定时间间隔】
**参数 **
command- 要执行的任务initialDelay- 延迟首次执行的时间period- 连续执行之间的时间间隔unit- initialDelay和period参数的时间单位
结果 ScheduledFuture表示一系列重复任务的待完成。 未来的get()方法永远不会正常返回,并且会在任务取消或任务执行异常终止时抛出异常。
**异常 ** RejectedExecutionException - 如果无法安排任务执行 NullPointerException - 如果命令或单位为空 IllegalArgumentException - 如果周期小于或等于零
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) 提交在给定的初始延迟之后首先启用的定期动作,并且随后在一次执行的终止和下一次执行的开始之间给定延迟。
public class DemoScheduedExecutorService {
public static void main(String[] args) {
ScheduledExecutorService scheduledPool= Executors.newScheduledThreadPool(3);
// 延时设定时间(initialDelay)后只执行一次任务。
scheduledPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println("延迟2秒");
}
},2, TimeUnit.SECONDS);
//延时设定时间(initialDelay)后执行第一次任务,随后每次任务执行完后延迟设定时间(delay)再次执行任务。
scheduledPool.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
System.out.println("延迟5秒后,重新设定间隔时间");
}
},5,3,TimeUnit.SECONDS);
//延时设定时间(initialDelay)后执行第一次任务,每隔时间间隔(period)再次执行任务。
scheduledPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("延迟1秒后每三秒执行一次");
}
},1,3,TimeUnit.SECONDS);
}
}newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) 返回一个线程池(这个线程池只有一个线程 ),这个线程 池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去!
ExecutorService接口
public interface ExecutorService 用来从线程池中获取线程,调用start方法,并执行任务
void shutdown() 关闭线程池 启动有序关闭,其中先前提交的任务将被执行,但不会接受任何新任务。 如果已经关闭,调用没有其他影响。 此方法不会等待先前提交的任务完成执行。 使用awaitTermination来做到这一点。
Future<?> submit(Runnable task) 执行任务 提交Runnable任务以执行并返回表示该任务的Future。 _成功_完成后,Future的get方法将返回null
有返回值 的任务必须实现
Callable 接口无返回值 的任务必须
Runnable 接口
执行 Callable 任务后,可以**获取一个 ** Future 的对象 ,在该对象上调用 get 就可以获取到 Callable 任务 返回的 Object 了,再结合线程池接口 ExecutorService 就可以实现传说中有返回结果的多线程V get() 如果需要等待计算完成,然后检索其结果。
public class MyCallable implements Callable {
@Override
public Object call() throws Exception {
return Thread.currentThread();
}
}
public class DemoFuture {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建一个线程池。里面有2个线程;
// 此方法只要不主动销毁线程,就算中断,也会创建一个新线程接替
ExecutorService pool= Executors.newFixedThreadPool(3);
//创建多个有返回值的任务
List<Future> list=new ArrayList<>();
for (int i = 0; i < 3; i++) {
Callable c=new MyCallable();
//执行任务并获取Future对象
Future f=pool.submit(c);
list.add(f);
}
//关闭线程池
pool.shutdown();
//获取所有并发任务的运行结果
for (Future future : list) {
//从Future对象上获取任务的返回值,并输出到控制台
System.out.println(future.get().toString());
}
}
}public class MyCallable implements Callable<Boolean> {
private Integer age;
public MyCallable(Integer age) {
this.age = age;
}
@Override
public Boolean call() throws Exception {
if (age>20){
return true;
}else{
return false;
}
}
}
//1.创建一个线程池,线程池中有多少个原始线程
ExecutorService pool= Executors.newFixedThreadPool(3);
Future<Boolean> submit1 = pool.submit(new MyCallable(19));
Future<Boolean> submit2 = pool.submit(new MyCallable(23));
System.out.println(submit1.get().toString());
System.out.println(submit2.get());
//关闭线程池
pool.shutdown();线程池的使用步骤
使用工厂类
Executors里面提供的静态方法生产指定数量的线程池创建一个类,实现Runnable接口,重写run方法,设置线程任务
调用
ExceutorService接口 里的submit方法,传递线程任务,开启线程执行run方法调用
ExceutorService接口 里的shutdown方法销毁线程池(不建议使用)
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName());
}
}
}
//1.创建一个线程池,线程池中有多少个原始线程
ExecutorService pool= Executors.newFixedThreadPool(3);
//调用submit方法
pool.submit(new MyRunnable());
//关闭线程池
pool.shutdown();线程池的组成
线程池管理器 :用于创建并管理线程池
工作线程 :线程池中的线程
任务接口 :每个任务必须实现的接口,用于工作线程调度其运行
任务队列 :用于存放待处理的任务,提供一种缓冲机制
Java 中的线程池是通过 Executor 框架实现的,该框架中用到了 Executor,Executors, ExecutorService,ThreadPoolExecutor ,Callable 和 Future、FutureTask 这几个类。

ThreadPoolExecutor 的构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler);
1. corePoolSize:指定了线程池中的线程数量。
2. maximumPoolSize:指定了线程池中的最大线程数量。
3. keepAliveTime:当前线程池数量超过 corePoolSize 时,多余的空闲线程的存活时间,即多次时间内会被销毁。
4. unit:keepAliveTime 的单位。
5. workQueue:任务队列,被提交但尚未被执行的任务。
6. threadFactory:线程工厂,用于创建线程,一般用默认的即可。
7. handler:拒绝策略,当任务太多来不及处理,如何拒绝任务。
JDK 内置的拒绝策略如下: new ThreadPoolExecutor XXXXXXXX()
1. AbortPolicy : 直接抛出异常,阻止系统正常运行。
2. CallerRunsPolicy : 只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降。
3. DiscardOldestPolicy : 丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务。
4. DiscardPolicy : 该策略默默地丢弃无法处理的任务,不予任何处理。如果允许任务丢失,这是最好的一种方案。
以上内置拒绝策略均实现了 RejectedExecutionHandler 接口,若以上策略仍无法满足实际需要,完全可以自己扩展 RejectedExecutionHandler 接口。public class DemoThreadPoolExecutor {
public static void main(String[] args) {
ThreadPoolExecutor executor=new ThreadPoolExecutor(2, // 线程数量
5, // 最大线程数量
30,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable(5),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy());
for (int i = 0; i < 16; i++) {
executor.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
}
System.out.println("线程池中的线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+executor.getQueue()+",已经执行完的线程数目:"+executor.getCompletedTaskCount());
executor.shutdown();
}
}
Java 线程池工作过程
线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面
有任务,线程池也不会马上执行它们。当调用 execute() 方法添加一个任务时,线程池会做如下判断:
a) 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
b) 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
c) 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要
创建非核心线程立刻运行这个任务;
d) 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池
会抛出异常 RejectExecutionException。当一个线程完成任务时,它会从队列中取下一个任务来执行。
当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运 行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它 最终会收缩到 corePoolSize 的大小。

Lambada表达式
JDK1.8新特性
格式
由三部分组成
一些参数
一个箭头
一段代码
(参数) -> {代码}
():接口(有且只有一个方法)中的抽象方法的参数列表;如果没有参数就空着,如果有多个参数,用逗号隔开
->:传递,把参数传递给方法体
{}:重写方法的方法体,方法体里面只有一行代码,{}可省略new Thread(new Runnable() {
@Override
public void run() {
System.out.println(222);
}
}).start();
//使用lambada
new Thread(
()->{
System.out.println(333);
}
).start();
//继续简化
new Thread(() -> System.out.println(333)).start();public interface Person {
void good(Integer age,String name);
}
public static void study(Person person){}
public static void main(String[] args) {
//函数式编程
study((Integer age,String name)->{
System.out.println(age);
System.out.println(name);
});
}CompletableFuture
supplyAsync 有返回结果,runAsync没有返回结果
allOf() 方法会等到所有的 CompletableFuture 都运行完成之后再返回
thenCompose() 可以链接两个 CompletableFuture 对象,并将前一个任务的返回结果作为下一个任务的参数,它们之间存在着先后顺序。
thenCombine() 会在两个任务都执行完成后,把两个任务的结果合并。两个任务是并行执行的,它们之间并没有先后依赖顺序
通过 handle() 方法来处理任务执行过程中可能出现的抛出异常的情况
CompletableFuture 详解 | JavaGuide 并行处理 (1)单个处理返回
//异步执行两个任务
CompletableFuture<User> supplyAsync = CompletableFuture.supplyAsync(() -> {
return getUser("汪浩天");
}, threadPoolExecutor);
supplyAsync.thenAccept(user -> {
log.info("task1 执行结果 {}", user.getUserName());
});(2)一起返回处理
//等待三个任务都完成,才执行
CompletableFuture<Void> taskUserAccountFuture = CompletableFuture.runAsync(() -> {
fillUserAccount();
}, threadPoolExecutor);
CompletableFuture<Void> taskMpOpenIdFuture = CompletableFuture.runAsync(() -> {
int i = 1 / 0;
fillMpOpenId();
}, threadPoolExecutor);
CompletableFuture<Void> taskUnionIdFuture = CompletableFuture.runAsync(() -> {
fillUnionId();
}, threadPoolExecutor);
CompletableFuture<Void> allFuture = CompletableFuture.allOf(taskUserAccountFuture, taskMpOpenIdFuture, taskUnionIdFuture);
allFuture.join();
log.info("{queryData} 任务执行完成!");//task2等待task1执行完成后,再执行
private static void task3() {
try {
// 创建第一个任务,直接返回 User 对象
CompletableFuture<User> future = CompletableFuture.supplyAsync(() ->
{
int i = 1 / 0;
return getUser("汪浩天");
}
, threadPoolExecutor)
.handle((user, throwable) -> {
if (throwable != null) {
log.error("task1 执行异常", throwable);
// 可以返回默认值或者抛出自定义异常
throw new BusinessException(ErrorCode.SYSTEM_ERROR, throwable.getMessage());
}
log.info("task1 执行结果 {}", user.getUserName());
return user;
});
// 等待第一个任务完成后执行第二个任务
future.thenCompose(firstResult ->
CompletableFuture.supplyAsync(() ->
getUser("涂鏊飞")
, threadPoolExecutor)
.handle((user, throwable) -> {
if (throwable != null) {
log.error("task2 执行异常", throwable);
throw new BusinessException(ErrorCode.SYSTEM_ERROR, throwable.getMessage());
}
log.info("task2 执行结果 {}", user.getUserName());
return user;
})
).join(); // 等待整个任务链完成
} catch (Exception e) {
log.error(e);
throw new BusinessException(ErrorCode.SYSTEM_ERROR, e.getMessage());
}
}