前言:准备发这篇文章时,看到某人发的朋友圈,回想这么多天的经历。意外,惊喜,内疚一涌而上。。。虽然时间已过,但我还想说声:“happy Birthday,little Prince,the happy always with you”。(来自一位木讷程序猿的祝福)
正文
在上一篇文章中,我们模拟的FutureTask执行多任务计算的使用场景存在一定的缺陷:
(1)我们使用List>收集任务,其中在调用阶段会循环查看结果,如果没有完成,那么get方法就会阻塞。此时会出现一个问题:
如果排在前面的任务没有完成, 那么就会阻塞, 这样后面已经完成的任务就没法获得结果了, 导致了不必要的等待时间。更为严重的是: 第一个任务如果几个小时或永远完成不了, 而后面的任务几秒钟就完成了, 那么后面的任务的结果都将得不到处理
优化代码:
使用CompletionService管理异步任务 (CompletionService将Executor和BlockQueue的功能融合在一起),ExecutorCompletionService实现了CompletionService,并将计算部分委托给了Executor。由于ExecutorCompletionService内部维护了一个BlockingQueue, 只有完成的任务才被加入到队列中,如果队列为空,则调用take()方法会阻塞;如果队列中有数据,就返回数据。这样就解决了上述由于某个任务耗时过长而导致取值时整体阻塞的情况。
函数解释:
CompletionService接口定义了一组任务管理接口:
submit() - 提交任务
take() - 获取任务结果
poll() - 获取任务结果
ExecutorCompletionService类是CompletionService接口的实现
ExecutorCompletionService内部管理者一个已完成任务的阻塞队列
ExecutorCompletionService引用了一个Executor, 用来执行任务
submit()方法最终会委托给内部的executor去执行任务
take/poll方法的工作都委托给内部的已完成任务阻塞队列,如果阻塞队列中有已完成的任务, take方法就返回任务的结果, 否则阻塞等待任务完成
poll与take方法不同, poll有两个版本:
(1)poll()方法: 如果有值就返回, 否则返回null
(2)poll(long timeout, TimeUnit unit)方法: 如果有值就返回, 否则等待指定的时间; 如果时间到了如果有值, 就返回值, 否则返回null
关于CompletionService和ExecutorCompletionService的类图如下
重新实现:
源码:
ExecutorCompletionService是如何执行任务, 又是如何将任务的结果存储到完成队列中的呢?
(1)初始化
ExecutorCompletionService在构造函数中创建一个BlockingQueue来保存计算完成的结果。
(2)提交任务
ExecutorCompletionService在submit任务时, 会创建一个QueueingFuture, 然后将创建的QueueingFuture丢给executor, 让executor完成任务的执行工作。
(注释:QueueingFuture继承与FutureTask类, 而FutureTask实现了两个接口Runnable和Future
Runnable一般表示要执行的任务的过程, 而Future则表述执行任务的结果 (或者说是任务的一个句柄, 可获取结果, 取消任务等,因此FutureTask就是一个有结果可期待的任务
)
(3)完成任务
调用QueueingFuture重写的done方法,并将结果放入BlockingQueue中
具体源码解释看博文:https://www.jianshu.com/p/cfda708a3478
领取专属 10元无门槛券
私享最新 技术干货