java concurrent之CompletionService

    在採用Executor執行任務時,可用通過採用Future來獲取單個任務執行的結果,在Future中提供了一個get方法,該方法在任務執行返回之前,將會阻塞。

當向Executor提交批處理任務時,並且希望在它們完成後獲得結果,如果用FutureTask,你可以針對這一組任務進行遍歷,並用future.get()去獲取結果,若當前任務沒有完成,則會造成阻塞。這對於對任務結果需要分別對待的時候是可行的。但是若所有task產生的結果都可以被同等看待,這時候遍歷這樣的方式顯然是不可行了,因爲遍歷時,後訪問的future的任務先於當前訪問的任務,由於future.get()方法是阻塞的,因此需要等待當前任務完成才能繼續後面的遍歷,這樣的話就會導致實效性不高。

顯然很多時候,一組task產生的結果都應該是沒有區別的,也就是滿足上述第二種情況。這個時候咋辦呢?jdk爲我們提供了一個很好的接口CompletionService
這個接口的具體實現類是ExecutorCompletionService。
該類中定義下面三個屬性
private final Executor executor;
private final AbstractExecutorService aes;
private final BlockingQueue<Future<V>> completionQueue;
executor由構造函數傳入,aes只是用於生成Future對象。特別要注意是completionQueue。
它維護了一個保存Future對象的BlockingQueue。當這個Future對象狀態是結束的狀態的時候,也就是task執行完成之後,會將它加入到這個Queue中。到底是在哪裏將完成的Future加入到隊列裏面的呢?又是怎麼知道task是什麼時候結束的呢?
在ExecutorCompletionService中定義了一個QueueingFuture類,該類的實現:

private class QueueingFuture extends FutureTask<Void> {
        QueueingFuture(RunnableFuture<V> task) {
            super(task, null);
            this.task = task;
        }
        protected void done() { completionQueue.add(task); }
        private final Future<V> task;
    }

可以看到在done方法中,它會把當前的task加入到阻塞隊列中。
追蹤done方法可以看到,該方法定義在FutureTask中,默認實現爲空,從註釋可以看出,當Future的狀態轉爲isDone的時候,就會調用該方法。

調用端在調用CompletionService的take方法時,實際上調用的是BlockingQueue的take方法,當隊列中有內容時,該方法會會立即返回隊列中的對象,當隊列爲空時,調用線程將會阻塞。而只要有任務完成,調用線程就會跳出阻塞,獲得結果。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章