线程池原理(五):CompletionService

来源:互联网 发布:淘宝店铺手机怎么登陆 编辑:程序博客网 时间:2024/05/24 06:10

任务提交到线程池后,将任务的Future放到一个List中,然后遍历List,通过Future的get方法得到返回值。如果在遍历过程中get方法阻塞,即使位于List后面的Future已经完成,遍历List的线程也要继续等待,这就对效率有比较大的影响。我们希望任务结束后,返回值能够立即被获取,而不是要等待其他任务结束。

CompletionService正是为此而生,实现中通过维护一个队列保存结束任务的Future,如果有任务结束,任务的Future会保存到队列中,从该队列中一定能拿到任务的返回结果。如果没有已经完成的任务,队列为空,取结果的线程会阻塞。

CompletionService

我们先看下接口CompletionService

public interface CompletionService<V> {    //提交任务    Future<V> submit(Callable<V> task);    //提交任务    Future<V> submit(Runnable task, V result);    //取下一个已经结束任务的返回值,如果没有则等待    //注意,该方法响应中断    Future<V> take() throws InterruptedException;    //取下一个已经结束任务的返回值,如果没有返回null    Future<V> poll();    //取下一个已经结束任务的返回值,如果没有最多等待timeout时间    Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException;}

该接口很简单,主要就是take、pool方法,取已经完成任务的返回值,接着我们看下jdk中该接口的实现类,ExecutorCompletionService。

ExecutorCompletionService

该类实现了CompletionService,维护一个阻塞队列(默认为LinkedBlockingQueue)保存已经完成的任务Future。worker执行完某个任务时会将任务的Future添加到该阻塞队列,阻塞队列按任务的完成顺序保存了任务的Future。取任务时,如果阻塞队列为空,说明没有已经完成的任务。看下该该类的实现:

public class ExecutorCompletionService<V> implements CompletionService<V> {    //任务的执行委托给了executor    private final Executor executor;    //如果executor继承自AbstractExecutorService,aes和executor指向同一个对象    //否则aes为空    private final AbstractExecutorService aes;    //保存完成任务的Future    private final BlockingQueue<Future<V>> completionQueue;    //CompletionService执行的任务继承了FutureTask,重写了done方法    //每当任务结束后将任务的Future加到completionQueue中    private class QueueingFuture extends FutureTask<Void> {        QueueingFuture(RunnableFuture<V> task) {            super(task, null);            this.task = task;        }        //任务结束后将Future加到队列中        protected void done() { completionQueue.add(task); }        private final Future<V> task;    }    //包装成FutureTask    private RunnableFuture<V> newTaskFor(Callable<V> task) {        if (aes == null)            return new FutureTask<V>(task);        else            return aes.newTaskFor(task);    }    //包装成FutureTask    private RunnableFuture<V> newTaskFor(Runnable task, V result) {        if (aes == null)            return new FutureTask<V>(task, result);        else            return aes.newTaskFor(task, result);    }    //构造函数,指定执行器executor    public ExecutorCompletionService(Executor executor) {        if (executor == null)            throw new NullPointerException();        this.executor = executor;        this.aes = (executor instanceof AbstractExecutorService) ?            (AbstractExecutorService) executor : null;        //用LinkedBlockingQueue保存完成任务的Future        this.completionQueue = new LinkedBlockingQueue<Future<V>>();    }    //构造函数,指定executor和queue    public ExecutorCompletionService(Executor executor,                                     BlockingQueue<Future<V>> completionQueue) {        if (executor == null || completionQueue == null)            throw new NullPointerException();        this.executor = executor;        this.aes = (executor instanceof AbstractExecutorService) ?            (AbstractExecutorService) executor : null;        this.completionQueue = completionQueue;    }    //提交任务,将任务包装成QueueingFuture后交给executor执行    public Future<V> submit(Callable<V> task) {        if (task == null) throw new NullPointerException();        RunnableFuture<V> f = newTaskFor(task);        executor.execute(new QueueingFuture(f));        return f;    }    public Future<V> submit(Runnable task, V result) {        if (task == null) throw new NullPointerException();        RunnableFuture<V> f = newTaskFor(task, result);        executor.execute(new QueueingFuture(f));        return f;    }    //从队列中取已经结束任务的返回值,如果队列为空则阻塞    public Future<V> take() throws InterruptedException {        return completionQueue.take();    }    //从队列中取已经结束任务的返回值,如果队列为空返回null    public Future<V> poll() {        return completionQueue.poll();    }    //从队列中取已经结束任务的返回值,如果超时,返回空    public Future<V> poll(long timeout, TimeUnit unit)            throws InterruptedException {        return completionQueue.poll(timeout, unit);    }}

ExecutorCompletionService实现也很简单,主要是QueueingFuture,将QueueingFuture作为任务来执行,QueueingFuture继承FutureTask,重写了done方法。done方法在FutureTask中是一个空方法,留给子类重写。任务完成后会调用done方法,将任务的Future加到队列中。

原创粉丝点击