Callable和Future

原文: http://blog.csdn.net/ghsau/article/details/7451464

接着上一篇繼續併發包的學習,本篇說明的是Callable和Future,它倆很有意思的,一個產生結果,一個拿到結果。 
       Callable接口類似於Runnable,從名字就可以看出來了,但是Runnable不會返回結果,並且無法拋出返回結果的異常,而Callable功能更強大一些,被線程執行後,可以返回值,這個返回值可以被Future拿到,也就是說,Future可以拿到異步執行任務的返回值,下面來看一個簡單的例子:

public class CallableAndFuture {
    public static void main(String[] args) {
        Callable<Integer> callable = new Callable<Integer>() {
            public Integer call() throws Exception {
                return new Random().nextInt(100);
            }
        };
        FutureTask<Integer> future = new FutureTask<Integer>(callable);
        new Thread(future).start();
        try {
            Thread.sleep(5000);// 可能做一些事情
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

       FutureTask實現了兩個接口,Runnable和Future,所以它既可以作爲Runnable被線程執行,又可以作爲Future得到Callable的返回值,那麼這個組合的使用有什麼好處呢?假設有一個很耗時的返回值需要計算,並且這個返回值不是立刻需要的話,那麼就可以使用這個組合,用另一個線程去計算返回值,而當前線程在使用這個返回值之前可以做其它的操作,等到需要這個返回值時,再通過Future得到,豈不美哉!這裏有一個Future模式的介紹:http://openhome.cc/Gossip/DesignPattern/FuturePattern.htm。 
       下面來看另一種方式使用Callable和Future,通過ExecutorService的submit方法執行Callable,並返回Future,代碼如下:

public class CallableAndFuture {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        Future<Integer> future = threadPool.submit(new Callable<Integer>() {
            public Integer call() throws Exception {
                return new Random().nextInt(100);
            }
        });
        try {
            Thread.sleep(5000);// 可能做一些事情
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

       代碼是不是簡化了很多,ExecutorService繼承自Executor,它的目的是爲我們管理Thread對象,從而簡化併發編程,Executor使我們無需顯示的去管理線程的生命週期,是JDK 5之後啓動任務的首選方式。 
       執行多個帶返回值的任務,並取得多個返回值,代碼如下:

public class CallableAndFuture {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newCachedThreadPool();
        CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(threadPool);
        for(int i = 1; i < 5; i++) {
            final int taskID = i;
            cs.submit(new Callable<Integer>() {
                public Integer call() throws Exception {
                    return taskID;
                }
            });
        }
        // 可能做一些事情
        for(int i = 1; i < 5; i++) {
            try {
                System.out.println(cs.take().get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

       其實也可以不使用CompletionService,可以先創建一個裝Future類型的集合,用Executor提交的任務返回值添加到集合中,最後遍歷集合取出數據,代碼略。更新於2016-02-05,評論中就這個說法引發了討論,其實是我沒有講清楚,抱歉。這裏再闡述一下:提交到CompletionService中的Future是按照完成的順序排列的,這種做法中Future是按照添加的順序排列的。所以這兩種方式的區別就像評論中fishjam所描述的那樣。

發佈了88 篇原創文章 · 獲贊 105 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章