實現Runnable接口的線程類與一個缺陷,就是在任務執行完以後沒法取得任務的返回值。 若是須要獲取執行結果,就必須經過共享變量或者使用線程通訊的方式來達到效果,這樣使用起來就比較麻煩 。因此,從JDK 1.5開始,java提供了Callable接口,該接口和Runnable接口相相似,提供了一個call()方法能夠做爲線程的執行體,可是call()方法要比run()方法更爲強大:# call()方法能夠有返回值;# call()方法能夠聲明拋出異常。那麼使用callable接口是如何獲取返回值的呢?html
既然Callable接口能夠看作是Runnable的「加強版」,那咱們先看看Runnable接口的實現,追根溯源也是搬磚的廣泛素質嘛~java
public interface Runnable { public abstract void run(); }
Runnable接口中只包含一個抽象方法run()返回值爲void, 因此在執行完任務以後沒法返回任何結果。接下來咱們能夠看看Callable接口編程
public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }
這是一個泛型接口,call()函數返回的類型就是傳遞進來的V類型。 那麼該如何使用Callable接口呢?Callable接口不是Runnable接口的子接口,因此Callable對象不能直接做爲Thread的target去運行;並且call方法還有返回值--call()方法並非直接調用。併發
JDK提供了Future接口來表明call()方法裏的返回值,而且爲Future接口提供了一個實現類FutureTask,該類實現了Future接口,並實現了Runnable接口,其實該類也是Future接口的惟一實現類,因此FutureTask對象能夠做爲Thread的target。Callable通常配合ExecutorService來使用的,在ExecutorService接口中聲明瞭若干個submit方法的重載版本:函數
/** * Submits a value-returning task for execution and returns a * Future representing the pending results of the task. The * Future's <tt>get</tt> method will return the task's result upon * successful completion. */ <T> Future<T> submit(Callable<T> task); /** * Submits a Runnable task for execution and returns a Future * representing that task. The Future's <tt>get</tt> method will * return the given result upon successful completion. */ <T> Future<T> submit(Runnable task, T result); /** * Submits a Runnable task for execution and returns a Future * representing that task. The Future's <tt>get</tt> method will * return <tt>null</tt> upon <em>successful</em> completion. */ Future<?> submit(Runnable task);
通常狀況下咱們使用第一個submit方法和第三個submit方法,第二個submit方法不多使用 。spa
這裏提一下ExecutorService的submit與execute方法的區別:ExecutorService的submit與execute方法都能執行任務,但在使用過程,發現其對待run方法拋出的異常處理方式不同。二者執行任務最後都會經過Executor的execute方法來執行,但對於submit,會將runnable物件包裝成FutureTask,其run方法會捕捉被包裝的Runnable Object的run方法拋出的Throwable異常,待submit方法所返回的的Future Object調用get方法時,將執行任務時捕獲的Throwable Object包裝成java.util.concurrent.ExecutionException來拋出。
而對於execute方法,則會直接拋出異常,該異常不能被捕獲,想要在出現異常時作些處理,能夠實現Thread.UncaughtExceptionHandler接口。.net
當將一個Callable的對象傳遞給ExecutorService的submit方法,則該call方法自動在一個線程上執行,而且會返回執行結果Future對象。一樣,將Runnable的對象傳遞給ExecutorService的submit方法,則該run方法自動在一個線程上執行,而且會返回執行結果Future對象,可是在該Future對象上調用get方法,將返回null。線程
Future就是對於具體的Runnable或者Callable任務的執行結果進行取消、查詢是否完成、獲取結果。必要時能夠經過get方法獲取執行結果,該方法會阻塞直到任務返回結果。code
Future類位於java.util.concurrent包下 :htm
public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
在Future接口中聲明瞭5個方法,下面依次解釋每一個方法的做用:
也就是說Future提供了三種功能:
1)判斷任務是否完成;
2)可以中斷任務;
3)可以獲取任務執行結果。
由於Future只是一個接口,因此是沒法直接用來建立對象使用的,所以就有了FutureTask。
咱們先來看一下FutureTask的實現:
public class FutureTask<V> implements RunnableFuture<V>
FutureTask類實現了RunnableFuture接口,咱們再來看一下RunnableFuture接口的實現:
public interface RunnableFuture<V> extends Runnable, Future<V> { void run(); }
能夠看出RunnableFuture繼承了Runnable接口和Future接口,而FutureTask實現了RunnableFuture接口。因此它既能夠做爲Runnable被線程執行,又能夠做爲Future獲得Callable的返回值
FutureTask提供了2個構造器:
public FutureTask(Callable<V> callable) { } public FutureTask(Runnable runnable, V result) { }
如何有童鞋對FutureTask具體內容感興趣,能夠看一下這篇文章:FutureTask深刻解析
1.使用Callable+Future獲取執行結果 :
public class CallableTest implements Callable<String>{ private int id; public CallableTest(int ThreadId){ id = ThreadId; } public String call(){ return "knock knock,who's there ? This is Thead " + id; } public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); ArrayList<Future<String>> results = new ArrayList<Future<String>>(); for(int i = 0; i < 10; i++){ results.add(exec.submit(new CallableTest(i))); } for(Future<String> fs : results){ try { System.out.println(fs.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }finally{ exec.shutdown(); } } } }
執行結果:
knock knock,who's there ? This is Thead 0 knock knock,who's there ? This is Thead 1 knock knock,who's there ? This is Thead 2 knock knock,who's there ? This is Thead 3 knock knock,who's there ? This is Thead 4 knock knock,who's there ? This is Thead 5 knock knock,who's there ? This is Thead 6 knock knock,who's there ? This is Thead 7 knock knock,who's there ? This is Thead 8 knock knock,who's there ? This is Thead 9
其中submit方法會產生Future對象,用Callable返回結果的特定類型進行了參數化。固然,也能夠用isDone()來查詢Future是否已經完成。當任務完成時,能夠調用get()方法來獲取該結果;也能夠直接調用get()方法,該種狀況下,get()將阻塞,直至結果準備就緒。
2.使用Callable+FutureTask獲取執行結果 :
public class CallableTest implements Callable<String>{ private int id; public CallableTest(int ThreadId){ id = ThreadId; } public String call(){ return "knock knock,who's there ? This is Thead " + id; } public static void main(String[] args) { ArrayList<FutureTask<String>> results = new ArrayList<FutureTask<String>>(); for(int i = 0; i < 10; i++){ FutureTask<String> ft = new FutureTask<>(new CallableTest(i)); new Thread(ft).start(); results.add(ft); } for(Future<String> fs : results){ try { System.out.println(fs.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } }
執行結果:
knock knock,who's there ? This is Thead 0 knock knock,who's there ? This is Thead 1 knock knock,who's there ? This is Thead 2 knock knock,who's there ? This is Thead 3 knock knock,who's there ? This is Thead 4 knock knock,who's there ? This is Thead 5 knock knock,who's there ? This is Thead 6 knock knock,who's there ? This is Thead 7 knock knock,who's there ? This is Thead 8 knock knock,who's there ? This is Thead 9
能夠看到,使用FutureTask對象來獲取返回結果的時候,該對象能夠做爲Thread中的target對象,因此能夠不使用 ExecutorService
來提交任務(實際上ExecutorService可能要比new Thread().start()方式要慢一點
),但運行的結果仍是同樣的。
參考文章: