开篇点题:
在 CompletableFuture 中带不带 Async 后缀,本质上都是异步执行。这里涉及到一个概念"异步",何为异步:指的是在不阻塞前一个任务,同时又可以让当前任务执行。什么意思呢?
我们通过 CompletableFutrue 中的thenApply和thenApplyAsync两个方法来举例子:
两者都是在上一个任务执行完成后执行一些你想要执行的动作。
thenApply方法在执行的时候并不会另外开启一个异步线程,而是使用当前线程执行thenApply中的逻辑。thenApplyAsync方法是额外的开启一个线程来执行你在thenApplyAsync方法中逻辑。
这里就奇怪了,thenApplyAsync可以说是异步能够理解,因为他单独开启了一个线程,不会等待上一个任务执行结束之后在执行。但是thenApply方法就使用一个线程然后执行两个任务,咋也是说异步执行呢?
其实要弄清楚这个问题,需要分析一个概念性的问题: "你说的异步是执行任务的异步?还是执行线程的异步? "。
首先,我们在使用thenApply方法的时候不管后缀带不带 Async,那么至少来说都是两个任务,否着那里用的上thenApply方法呢?所以,我可以分析得出:
- 执行任务的异步:这里不是的异步的,因为,在执行这两任务的时候,必定是需要上一个任务执行完,下一个任务获取到上一个任务执行的结果,才能执行,你说这不是等待嘛?那里的异步可言》
- 执行线程的异步:这里是异步的,因为,不管你是否使用 Async 后缀的方法,第二个任务也就是
thenApply/thenApplyAsync这两个方法中的执行逻辑,都是被包在同一个Task中,那么第一个任务执行完毕当需要执行第二个任务的时候,完全是看 CPU 调度,如果有空就执行第二个任务,没空就等等。
我们看图再进行理解:
thenApply:
CompletableFuture
.supplyAsync(() -> {
System.out.println("任务一:[" + "线程ID:" + Thread.currentThread().getId() + "--线程名称:" + Thread.currentThread().getName() + "]");
return "task1";
})
.thenApply(task1 -> {
System.out.println("任务二:[" + "线程ID:" + Thread.currentThread().getId() + "--线程名称:" + Thread.currentThread().getName() + "]");
return "合并:" + task1 + "task2";
});
执行结果:
分析一下:
- 对于任务来说:阻塞-->>这没啥好说,任务二不等待任务一执行结束,那来的执行机会?
- 对于线程来说:不阻塞-->>可以看到任务二使用的main线程,并不是ForkJoinPool中的线程,也就是使用了主线程来执行任务二,并不是使用线程池中的线程来完成任务二。这里就导致了一个问题,为了任务一执行结束后,需要任务二执行,结果好了main 线程被其他程序的任务使用着,那任务二不得等着那个程序用完 main 再轮到自己嘛。所以为什么说
thenApply方法不是异步的。
thenApplyAsync:
CompletableFuture
.supplyAsync(() -> {
System.out.println("任务一:[" + "线程ID:" + Thread.currentThread().getId() + "--线程名称:" + Thread.currentThread().getName() + "]");
return "task1";
})
.thenApply(task1 -> {
System.out.println("任务二:[" + "线程ID:" + Thread.currentThread().getId() + "--线程名称:" + Thread.currentThread().getName() + "]");
return "合并:" + task1 + "task2";
});
执行结果:
分析一下:
- 对于任务来说:阻塞-->>这没啥好说,任务二不等待任务一执行结束,那来的执行机会?
- 对于线程来说:阻塞/不阻塞-->>为什么说两个情况都可能存在,原因其实在上面的时候说过了,有可能任务二要执行的时候,正好有其他的程序正在使用着 main 线程,那么势必就会导致任务二要等待 main 线程有空的时候来执行自己。
虽然这个例子很简单,但是其实我不管是在面试中还是在教学视频中发现很多人对这个概念都是不清楚的,只知道加个async后缀就是异步,但是并没有深入思考,他这异步到底是什么意思。
希望这个简短的小案例可以加深同学对于并发编程中的异步含义的理解:)。