我们现在在Java中使用多线程通常不会直接用Thread对象了,而是会用到java.util.concurrent包下的ExecutorService类来初始化一个线程池供我们使用。
当我们使用ExecutorService来做多线程处理时,习惯自己维护一个list保存submit的callable task所返回的Future对象。
然后在主线程中遍历这个list并调用Future的get()方法取到Task的返回值。
这两者最主要的区别在于submit的task不一定是按照加入自己维护的list顺序完成的。
从list中遍历的每个Future对象并不一定处于完成状态,这时调用get()方法就会被阻塞住,如果系统是设计成每个线程完成后就能根据其结果继续做后面的事,这样对于处于list后面的但是先完成的线程就会增加了额外的等待时间。
比如:浏览器在渲染某个页面时,需要下载很多图片,我们把下载图片放到ExecutorService的tast里面处理,这样我们只能按照放入list的图片顺序来渲染图片,如果有些图片比较大,又比较早的被放入的提交的list<task>,这时候就比较影响用户体验了。
如果我们改用completionService,那就可以通过调用completionService.take(),达到只有有图片下载成功,我们就可以优先渲染(展示)这张图片的目的(我想浏览器应该也是按照逻辑来设计的吧)。
我们来看下CompletionService的一个具体实现的一个具体实现ExecutorCompletionService,
1、private final BlockingQueue<Future<V>> completionQueue; //维护一个阻塞队列
2、protected void done() { completionQueue.add(task); }//当某个task完成的时候,就把该task放入队列中
3、public Future<V> take() throws InterruptedException {
return completionQueue.take();
} //从阻塞队列中拿到已经完成的task
这样整个功能就很清晰了。