建立線程的2種方式,一種是直接繼承Thread,另一種就是實現Runnable接口。
這2種方式都有一個缺陷就是:在執行完任務以後沒法獲取執行結果。
若是須要獲取執行結果,就必須經過共享變量或者使用線程通訊的方式來達到效果,這樣使用起來就比較麻煩。
自從Java 1.5開始,就提供了Callable和Future,經過它們能夠在任務執行完畢以後獲得任務執行結果。java
Callable接口表明一段能夠調用並返回結果的代碼;Future接口表示異步任務,是尚未完成的任務給出的將來結果。因此說Callable用於產生結果,Future用於獲取結果。
Callable接口使用泛型去定義它的返回類型。Executors類提供了一些有用的方法在線程池中執行Callable內的任務。因爲Callable任務是並行的(並行就是總體看上去是並行的,其實在某個時間點只有一個線程在執行),咱們必須等待它返回的結果。
java.util.concurrent.Future對象爲咱們解決了這個問題。在線程池提交Callable任務後返回了一個Future對象,使用它能夠知道Callable任務的狀態和獲得Callable返回的執行結果。Future提供了get()方法讓咱們能夠等待Callable結束並獲取它的執行結果。異步
java.lang.Runnable吧,它是一個接口,在它裏面只聲明瞭一個run()方法:ide
public interface Runnable { public abstract void run(); }
因爲run()方法返回值爲void類型,因此在執行完任務以後沒法返回任何結果。函數
Callable位於java.util.concurrent包下,它也是一個接口,在它裏面也只聲明瞭一個方法,只不過這個方法叫作call():this
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類型。線程
通常狀況下是配合ExecutorService來使用的,在ExecutorService接口中聲明瞭若干個submit方法的重載版本。code
<T> Future<T> submit(Callable<T> task); <T> Future<T> submit(Runnable task, T result); Future<?> submit(Runnable task);
第一個submit方法裏面的參數類型就是Callable。對象
暫時只須要知道Callable通常是和ExecutorService配合來使用的,具體的使用方法講在後面講述。繼承
通常狀況下咱們使用第一個submit方法和第三個submit方法,第二個submit方法不多使用。接口
Future就是對於具體的Runnable或者Callable任務的執行結果進行取消、查詢是否完成、獲取結果。必要時能夠經過get方法獲取執行結果,該方法會阻塞直到任務返回結果。
Future類位於java.util.concurrent包下,它是一個接口:
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只是一個接口,因此是沒法直接用來建立對象使用的,所以就有了下面的FutureTask。
public interface RunnableFuture<V> extends Runnable, Future<V> { void run(); }
能夠看到這個接口實現了Runnable和Future接口,接口中的具體實現由FutureTask來實現。這個類的兩個構造方法以下 :
public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); sync = new Sync(callable); } public FutureTask(Runnable runnable, V result) { sync = new Sync(Executors.callable(runnable, result)); }
如上提供了兩個構造函數,一個以Callable爲參數,另一個以Runnable爲參數。這些類之間的關聯對於任務建模的辦法很是靈活,容許你基於FutureTask的Runnable特性(由於它實現了Runnable接口),把任務寫成Callable,而後封裝進一個由執行者調度並在必要時能夠取消的FutureTask。
FutureTask能夠由執行者調度,這一點很關鍵。它對外提供的方法基本上就是Future和Runnable接口的組合:get()、cancel、isDone()、isCancelled()和run(),而run()方法一般都是由執行者調用,咱們基本上不須要直接調用它。
public class MyCallable implements Callable<String> { private long waitTime; public MyCallable(int timeInMillis){ this.waitTime=timeInMillis; } @Override public String call() throws Exception { Thread.sleep(waitTime); //return the thread name executing this callable task return Thread.currentThread().getName(); } }
public class FutureTaskExample { public static void main(String[] args) { MyCallable callable1 = new MyCallable(1000); // 要執行的任務 MyCallable callable2 = new MyCallable(2000); FutureTask<String> futureTask1 = new FutureTask<String>(callable1);// 將Callable寫的任務封裝到一個由執行者調度的FutureTask對象 FutureTask<String> futureTask2 = new FutureTask<String>(callable2); ExecutorService executor = Executors.newFixedThreadPool(2); // 建立線程池並返回ExecutorService實例 executor.execute(futureTask1); // 執行任務 executor.execute(futureTask2); while (true) { try { if(futureTask1.isDone() && futureTask2.isDone()){// 兩個任務都完成 System.out.println("Done"); executor.shutdown(); // 關閉線程池和服務 return; } if(!futureTask1.isDone()){ // 任務1沒有完成,會等待,直到任務完成 System.out.println("FutureTask1 output="+futureTask1.get()); } System.out.println("Waiting for FutureTask2 to complete"); String s = futureTask2.get(200L, TimeUnit.MILLISECONDS); if(s !=null){ System.out.println("FutureTask2 output="+s); } } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }catch(TimeoutException e){ //do nothing } } } }