CompletableFuture执行线程的一次研究
来源:互联网 发布:sql数据库服务器搭建 编辑:程序博客网 时间:2024/06/06 13:02
在研究vertx线程模型的时候我开始注意到在vertx内部提供给开发者的多数异步api中用到了CompletableFuture或者Promise或者Future。以前也用过CompletableFuture和Futrue等,但当时没有想过它执行时的线程情况,于是写了个测试类用于了解它:
public class VertxTest { Logger logger = LoggerFactory.getLogger(VertxTest.class); @Test public void test() { System.out.println(Thread.currentThread().getName() + " | start"); testAsync(1000, result -> { System.out.println(Thread.currentThread().getName() + " || " + result); }); CompletableFuture future1 = testAsync(10000); future1.thenAccept(result -> { System.out.println(Thread.currentThread().getName() + "|||" + result); }); System.out.println(Thread.currentThread().getName() + "|||| end..." ); while (true) {} } private void testAsync(int max, Handler<AsyncResult<Float>> handler) { Thread thread = new Thread(() -> { float result = 0; for (int i = 0; i < max; i++) { result += 0.5; } handler.handle(Future.succeededFuture(result)); }); thread.start(); } private CompletableFuture testAsync(int max) { CompletableFuture future = new CompletableFuture(); new Thread(() -> { System.out.println(Thread.currentThread().getName() + " inner Thread"); float result = 0; for (int i = 0; i < max; i++) { result += 0.5; } future.complete(result); }).start(); return future; }}
在我电脑上的执行结果:
main | startThread-0 || 500.0Thread-1 inner Threadmain|||5000.0main|||| end...
对于
Thread-0 || 500.0和
Thread-1 inner Thread很好理解,在我new的那个线程中执行。
但对于
main|||5000.0
会有个问题:当把参数从10000调到100000000时,我电脑上的结果是:
main | startThread-0 || 500.0Thread-1 inner Threadmain|||| end...Thread-1|||8388608.0
请注意,对于
future1.thenAccept(result -> { System.out.println(Thread.currentThread().getName() + "|||" + result); });
中的那句打印,也就是传进去的这个Handler实例的handle(String param)调用,是在Thread-1中。而上面参数是10000时这个调用在main线程里。
为什么会有这个不同呢,或者说什么机制在调用future1.thenAccept(Handler handler)时,决定handler.handler(...)的调用要在哪个线程?
查看thenAccept的源代码,跟踪进去看到下面的片段:
private CompletableFuture<Void> uniAcceptStage(Executor e, Consumer<? super T> f) { if (f == null) throw new NullPointerException(); CompletableFuture<Void> d = new CompletableFuture<Void>(); if (e != null || !d.uniAccept(this, f, null)) { UniAccept<T> c = new UniAccept<T>(e, d, this, f); push(c); c.tryFire(SYNC); } return d; }
这里的逻辑就是,如果调用thenAccept方法时,结果已经得到了,则直接返回,由当前调用thenAccept的线程调用handler.handle(...),否则会将传进来的handler包装成一个UniAccept<T>对象,放入到一个stack里,等待我new的那个线程执行完之后回调handler.handle(...),也就不在main线程中了。
这就解释了为什么我测试例子中当参数调大之后不是在main中执行那个打印,而是在一个新的线程中的缘故。CompletableFuture这么做的好处在于,对时间短的处理,直接返回结果,而对于“询问”结果时还没有计算出来的,则可以非阻塞的继续往下执行等待结果执行完了回调。
对于多线程编程,清楚你写的代码块在哪个线程上下文环境中执行是很重要的,所以了解了CompletableFuture的thenAccept()方法的这个机制,将会大有好处。CompletableFuture还提供了很多有用的方法,其机制也可以和thenAccept机制比较思考。
0 0
- CompletableFuture执行线程的一次研究
- CompletableFuture 的并发性能研究
- 理解 CompletableFuture 的任务与回调函数的线程
- Java8的CompletableFuture之一
- Java8的CompletableFuture
- 一个CompletableFuture的例子
- CompletableFuture
- CompletableFuture
- CompletableFuture
- CompletableFuture
- 一次执行选择了错误的索引的研究与困惑
- Java8的CompletableFuture之二
- Java8的CompletableFuture异步调用
- GCD的定义及使用详解(同步异步、并发串行、线程间通信、延时执行、只执行一次代码)
- C# 线程 的研究
- 线程池的研究
- Java8 CompletableFuture组合式的编程(笔记)
- 一次"现场调查"用户研究的感触
- 每日一题(5)poj,1258, AgriNet
- Hadoop&Spark解决二次排序
- 第一篇 微信商城 开发前的准备工作
- 使用C语言的libcurl库和cJSON库来在线查询CVE漏洞代码实现
- oracle的license和正版使用问题(收集整理)
- CompletableFuture执行线程的一次研究
- shell 30分钟删除文件demo
- oracle11g OCP 认证 1Z0-052考试笔记3
- Eclipse - changing font size in project/package explorer
- CSS中first-child和nth-child的讲解
- 设计模式--命令模式
- Windows同时使用Python 2.7和Python 3.5以及pip
- java中使用反射获取对象的属性和属性值
- J2SE第四章——异常(二)