【Java線程】Callable和Future

Future模式

Future接口是Java線程Future模式的實現,能夠來進行異步計算。java

Future模式能夠這樣來描述:web

我有一個任務,提交給了Future,Future替我完成這個任務。期間我本身能夠去作任何想作的事情。一段時間以後,我就即可以從Future那兒取出結果。併發

就至關於下了一張定貨單,一段時間後能夠拿着提訂單來提貨,這期間能夠幹別的任何事情。其中Future接口就是定貨單,真正處理訂單的是Executor類,它根據Future接口的要求來生產產品。dom


Callable和Future接口

Callable接口

Callable和Future一個產生結果,一個拿到結果。異步

Callable接口相似於Runnable,可是Runnable不會返回結果,而Callable能夠返回結果,這個返回值能夠被Future拿到,也就是說,Future能夠拿到異步執行任務的返回值。ui

  •  V call()spa

  1. /** .net

  2.  * Computes a result, or throws an exception if unable to do so. 線程

  3.  * 設計

  4.  * @return computed result 

  5.  * @throws Exception if unable to compute a result 

  6.  */  

  7. V call() throws Exception;  



Future接口

Future 表示異步計算的結果。Future接口中有以下方法:

  •     boolean cancel(boolean mayInterruptIfRunning)

取消任務的執行。參數指定是否當即中斷任務執行,或者等等任務結束

  •     boolean isCancelled() 

任務是否已經取消,任務正常完成前將其取消,則返回 true

  •     boolean isDone()

任務是否已經完成。須要注意的是若是任務正常終止、異常或取消,都將返回true

  •     V get()

等待任務執行結束,而後得到V類型的結果。InterruptedException 線程被中斷異常, ExecutionException任務執行異常,若是任務被取消,還會拋出CancellationException

  •     V get(long timeout, TimeUnit unit) 

同上面的get功能同樣,多了設置超時時間。參數timeout指定超時時間,uint指定時間的單位,在枚舉類TimeUnit中有相關的定義。若是計算超時,將拋出TimeoutException

Future接口提供方法來檢測任務是否被執行完,等待任務執行完得到結果。也能夠設置任務執行的超時時間,這個設置超時的方法就是實現Java程序執行超時的關鍵。

因此,若是須要設定代碼執行的最長時間,即超時,能夠用Java線程池ExecutorService類配合Future接口來實現。

  1. int result = future.get(5000, TimeUnit.MILLISECONDS);   



Future實現類:SwingWorker

SwingWorker的用法

http://blog.csdn.net/vking_wang/article/details/8994882


Future實現類:FutureTask

Future的實現類有java.util.concurrent.FutureTask<V>即 javax.swing.SwingWorker<T,V>。一般使用FutureTask來處理咱們的任務。

FutureTask類同時又實現了Runnable接口,因此能夠直接提交給Thread、Executor執行。


  1. public class CallableAndFuture {    

  2.     public static void main(String[] args) {    

  3.         Callable<Integer> callable = new Callable<Integer>() {    

  4.             public Integer call() throws Exception {    

  5.                 return new Random().nextInt(100);    

  6.             }    

  7.         };   

  8.   

  9.         FutureTask<Integer> future = new FutureTask<Integer>(callable);    

  10.         new Thread(future).start();    

  11.   

  12.         try {    

  13.             Thread.sleep(5000);// 可能作一些事情    

  14.   

  15.             int result = future.get());    

  16.   

  17.         } catch (InterruptedException e) {    

  18.             e.printStackTrace();    

  19.         } catch (ExecutionException e) {    

  20.             e.printStackTrace();    

  21.         }    

  22.     }    

  23. }    


經過ExecutorService的submit方法執行Callable,並返回Future

使用ExecutorService


  1. public class CallableAndFuture {    

  2.     public static void main(String[] args) {   

  3.   

  4.         //ExecutorService.submit()  

  5.         ExecutorService threadPool = Executors.newSingleThreadExecutor();    

  6.         Future<Integer> future = threadPool.submit(new Callable<Integer>() {    

  7.             public Integer call() throws Exception {    

  8.                 return new Random().nextInt(100);    

  9.             }    

  10.         });   

  11.   

  12.         try {    

  13.             Thread.sleep(5000);// 可能作一些事情    

  14.   

  15.             int result = future.get()); //Future.get()  

  16.   

  17.         } catch (InterruptedException e) {    

  18.             e.printStackTrace();    

  19.         } catch (ExecutionException e) {    

  20.             e.printStackTrace();    

  21.         }    

  22.     }    

  23. }    


若是要執行多個帶返回值的任務,並取得多個返回值,可用CompletionService:

CompletionService至關於Executor加上BlockingQueue,使用場景爲當子線程併發了一系列的任務之後,主線程須要實時地取回子線程任務的返回值並同時順序地處理這些返回值,誰先返回就先處理誰。


  1. public class CallableAndFuture {    

  2.     public static void main(String[] args) {    

  3.         ExecutorService threadPool = Executors.newCachedThreadPool();    

  4.         CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(threadPool);    

  5.         for(int i = 1; i < 5; i++) {    

  6.             final int taskID = i;    

  7.             //CompletionService.submit()  

  8.             cs.submit(new Callable<Integer>() {    

  9.                 public Integer call() throws Exception {    

  10.                     return taskID;    

  11.                 }    

  12.             });    

  13.         }    

  14.         // 可能作一些事情    

  15.         for(int i = 1; i < 5; i++) {    

  16.             try {    

  17.                 int result = cs.take().get());  //CompletionService.take()返回Future  

  18.             } catch (InterruptedException e) {    

  19.                 e.printStackTrace();    

  20.             } catch (ExecutionException e) {    

  21.                 e.printStackTrace();    

  22.             }    

  23.         }    

  24.     }    

  25. }          


或者不使用CompletionService:先建立一個裝Future類型的集合,用Executor提交的任務返回值添加到集合中,最後便利集合取出數據。


區別:

Future集合方法,submit的task不必定是按照加入本身維護的list順序完成的。從list中遍歷的每一個Future對象並不必定處於完成狀態,這時調用get()方法就會被阻塞住,若是系統是設計成每一個線程完成後就能根據其結果繼續作後面的事,這樣對於處於list後面的可是先完成的線程就會增長了額外的等待時間。

而CompletionService的實現是維護一個保存Future對象的BlockingQueue。只有當這個Future對象狀態是結束的時候,纔會加入到這個Queue中,take()方法其實就是Producer-Consumer中的Consumer。它會從Queue中取出Future對象,若是Queue是空的,就會阻塞在那裏,直到有完成的Future對象加入到Queue中。

因此,先完成的一定先被取出。這樣就減小了沒必要要的等待時間。

相關文章
相關標籤/搜索