Executor採用Runnable做爲基本的表達形式,雖然Runnable的run方法可以寫入日誌,寫入文件,寫入數據庫等操做,可是它不能返回一個值,或者拋出一個受檢查的異常,有些須要返回值的需求就不能知足了。java
Executor中的任務有四個狀態:建立,提交,開始和完成。若是說有些任務執行時間比較長,但願可以取消該任務,Executor中的任務在未開始前是能夠取消的,若是已經開始了,只能經過中斷的方式來取消。若是使用Callable和Future的結合,可使用Future的canel方法取消任務,這樣就方便多了。數據庫
import java.util.concurrent.*; public class Demo1 { public static void main(String args[]) throws Exception { ServiceTask task = new ServiceTask(); ExecutorService executor = Executors.newCachedThreadPool(); Future<Integer> result = executor.submit(task); executor.shutdown(); System.out.println("正在執行任務"); Thread.sleep(1000); System.out.println("task運行結果爲:" + result.get()); } } class ServiceTask implements Callable<Integer>{ @Override public Integer call() throws Exception { Thread.sleep(2000); int result = 0; // 假設一個很龐大的計算 for(int i=1;i<100;i++){ for (int j=0;j<i;j++){ result +=j; } } return result; } }
看一下執行結果:編程
這個例子就是一個很是簡單的使用Callable和Futute的例子,ServiceTask類實現了Callable接口,並返回一個Integer類型的值。併發
Future<Integer> result = executor.submit(task);這行代碼就是構造一個Future。使用其get()方法就能獲得最後的運行值。框架
好了看完這一個簡單的例子,那就來仔細瞭解一下它們。ide
來看一下callable的代碼:性能
public abstract interface Callable<V> { public abstract V call() throws Exception; }
能夠看出它是接口,提到接口就能夠明白接口是靈活的,支持傳入泛型參數。這個沒什麼,咱們來重點介紹一下Futurespa
首先來看關於它的介紹
Future提供了檢查計算是否完成的方法,以等待計算的完成,並獲取計算的結果。計算完成後只能使用 get 方法來獲取結果,若有必要,計算完成前能夠阻塞此方法。取消則由 cancel 方法來執行。還提供了其餘方法,以肯定任務是正常完成仍是被取消了。線程
來看一下Future的代碼日誌
public abstract interface Future<V> { public abstract boolean cancel(boolean paramBoolean); public abstract boolean isCancelled(); public abstract boolean isDone(); public abstract V get() throws InterruptedException, ExecutionException; public abstract V get(long paramLong, TimeUnit paramTimeUnit) throws InterruptedException, ExecutionException, TimeoutException; }
提供了五個方法
public abstract boolean cancel(boolean paramBoolean)
試圖取消任務的執行(注意是試圖),由於存在一些任務已完成、已取消或者由於某些緣由沒法取消的因素,存在着取消失敗的可能性。
當canel方法起做用時,有兩個狀況:
1.任務未開始,則該任務將永遠不會運行;
2.任務處於執行狀態,paramBoolean表示是否採用中斷的方式中斷線程。
public abstract boolean isCancelled()
若是任務正常取消的,則返回true。
public abstract boolean isDone();
若是任務已完成,則返回 true。 可能因爲正常終止、異常或取消而完成,在全部這些狀況中,此方法都將返回 true。
(注意若是調用isCanle方法,那麼isDone將始終返回true).
public abstract V get() throws InterruptedException, ExecutionException;
重點到了!這是Future獲取計算結果的方式之一,使用get方法。(注意這裏返回的是Callable中的泛型)
get方法取決於任務的狀態(未開始,運行中,已完成),若是任務已經完成,那麼get會當即返回或者拋出一個異常;
若是任務沒有完成,那麼get將阻塞知道任務完成。若是任務拋出了異常,那麼get會將該異常封裝成ExecutionException拋出。
public abstract V get(long paramLong, TimeUnit paramTimeUnit) throws InterruptedException, ExecutionException, TimeoutException;
若是須要在給定時間後獲取計算結果,可使用這個方法,若是超過給定時間以後沒有獲得計算結果,則拋出TimeoutException。(注意這裏返回的是Callable中的泛型)
來看代碼:
import java.util.concurrent.*; public class Demo1 { public static void main(String args[]) throws Exception { // 1.先實例化任務對象 ServiceTask task = new ServiceTask(); // 2.實例化Executor框架中的線程池 ExecutorService executor = Executors.newCachedThreadPool(); // 3.使用submit方法將任務提交(返回的是一個Future) Future<Integer> result = executor.submit(task); // 4.記得關閉線程池 executor.shutdown(); System.out.println("正在執行任務"); Thread.sleep(1000); // 5.打印最後的結果 System.out.println("task運行結果爲:" + result.get()); } } /** * Callable的實現類 */ class ServiceTask implements Callable<Integer>{ @Override public Integer call() throws Exception { Thread.sleep(2000); int result = 0; // 假設一個很龐大的計算 for(int i=1;i<100;i++){ for (int j=0;j<i;j++){ result +=j; } } return result; } }
運行結果:
接下來咱們來試一下定時取結果:
仍是在原來的代碼上修改:
import java.util.concurrent.*; public class Demo1 { public static void main(String args[]) throws Exception { // 1.先實例化任務對象 ServiceTask task = new ServiceTask(); // 2.實例化Executor框架中的線程池 ExecutorService executor = Executors.newCachedThreadPool(); // 3.使用submit方法將任務提交(返回的是一個Future) Future<Integer> result = executor.submit(task); // 4.記得關閉線程池 executor.shutdown(); System.out.println("正在執行任務"); Thread.sleep(1000); // 5.設置定時一秒取結果 System.out.println("task運行結果爲:" + result.get(1,TimeUnit.MILLISECONDS)); } } /** * Callable的實現類 */ class ServiceTask implements Callable<Integer>{ @Override public Integer call() throws Exception { //這裏睡眠2秒 Thread.sleep(2000); int result = 0; // 假設一個很龐大的計算 for(int i=1;i<100;i++){ for (int j=0;j<i;j++){ result +=j; } } return result; } }
來提早猜測一下,首先設置了定時一秒以後取得結果,可是ServiceTask設置兩秒的睡眠時間,理應取結果失敗,看一下運行結果:
是的,若是在規定時間內沒法取到結果,就會返回TimeoutException。
FutureTask是Future的實現類,它繼承了RunnableFuture,RunnableFuture實際上繼承了Runnable和Future接口。
來看一下使用如何FutureTask:
import java.util.concurrent.*; public class FutureCallDemo2 { public static void main(String args[])throws Exception{ // 1.先實例化任務對象 FutureTaskService task = new FutureTaskService(); // 2.實例化Executor框架中的線程池 ExecutorService excutor = Executors.newCachedThreadPool(); // 3.直接new一個FutureTask FutureTask<Long> result = new FutureTask<Long>(task); // 4.提交任務 excutor.submit(result); // 5.關閉線程池 excutor.shutdown(); System.out.println("主線程正在執行任務"); System.out.println("task運行結果爲:" + result.get()); } } /** * 繼承Callable接口 */ class FutureTaskService implements Callable<Long> { @Override public Long call() throws Exception { Thread.sleep(3000); // 10的階乘 long sum = 1; for (int i = 1; i <= 10; i++) { sum = sum * i; } return sum; } }
用法的話其實差很少。
Future和Callable能夠實現異構任務,可是有不少值得考慮的地方。
好比一個類使用了兩個任務,一個負責渲染頁面,一個負責下載圖像。
僞代碼以下:
//經過獲取圖像 List<ImageData>ImageDataList = future.get(); for(ImageData data:ImageDataList ){ //渲染頁面 renderPage(data); }
看似並行的執行任務,可是卻存在着問題。若是說下載圖像的速度遠小於渲染頁面的速度,那麼最終的執行速度就和串行無異了。
因此只有當大量相互獨立且同構的任務能夠進行併發處理時,才能體現出將任務分到多個任務中帶來的性能提高,考慮實際狀況再選擇使用會帶來事半功倍的效果。
本文參考:
Java併發編程實戰