java中的 Future詳解(以及ExecutorService中的各種方法)
關於這一塊一前一直是一個盲點,看了看原始碼總結一下把。這一塊內容和執行緒池也是息息相關的
執行緒池的頂級介面是Executor介面,裡面只有一個未實現方法是
void execute(Runnable command);
下來是ExecutorService介面,繼承自Executor介面,裡面多 了很多方法,比較重要的幾個方法是
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
提交多個任務,並返回與每個任務對應的Futue。也就是說,任務彼此之間不會相互影響,可以通過future跟蹤每一個任務的執行情況,比如是否被取消,是正常完成,還是異常完成。呼叫該方法的執行緒會阻塞,直到tasks全部執行完成(正常完成/異常退出)
如果執行緒在等待invokeAll執行的過程中被中斷,那麼執行緒池就會終止所以正在被執行的任務,並丟擲異常。
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;
給定的超時期滿,還沒有完成的任務會被取消,即Future.isCancelled()返回true;在超時期之前,無論是正常完成還是異常終止的任務,Future.isCancelled()返回false。
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
提交多個任務,一旦有一個任務完成,就會終止其他任務的執行,如果沒有一個任務完成,那麼就會丟擲異常。
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
如果在超時之前,所有任務已經都是異常終止,那就沒有必要在等下去了;如果超時之後,仍然有正在執行或等待執行的任務,那麼會丟擲TimeoutException。
void shutdown();
終止當前執行緒池,但是執行中的任務會執行到結束,等待的任務不被執行
List<Runnable> shutdownNow();
終止當前執行緒池,執行中的任務立即結束,等待的任務不被執行
boolean isShutdown();
如果呼叫了上面的兩個shutdown方法,就返回true
boolean isTerminated();
如果在shutdown之後,所以任務也都結束了,執行緒池處於終結狀態,那麼返回true
<T> Future<T> submit(Runnable task, T result);
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
上面的三個方法都是線上程池中提交一個任務,包括callable型別或者是runable型別。返回future物件
AbstractExecutorService抽象類實現了ExecutorService,對上面的submit方法也有了實現,其最終還是在呼叫頂級介面Executor中的execute方法,但是AbstractExecutorService並沒有實現execute方法,該方法在他的實現類ThreadPoolExecutor中實現。
從上面可以看到submit的引數型別有兩種分別是runable和callable,但是在AbstractExecutorService中對submit實現上,都是將兩種物件轉化成了FutureTask物件,然後將這個轉化之後的物件傳入execute方法中。
為啥runnable物件和callable物件可以轉化成futureTask物件呢?下面從Future介面說起。這個介面的方法如下:
cancel方法用來取消任務,如果取消任務成功則返回true,如果取消任務失敗則返回false。引數mayInterruptIfRunning表示是否允許取消正在執行卻沒有執行完畢的任務,如果設定true,則表示可以取消正在執行過程中的任務。如果任務已經完成,則無論mayInterruptIfRunning為true還是false,此方法肯定返回false,即如果取消已經完成的任務會返回false;如果任務正在執行,若mayInterruptIfRunning設定為true,則返回true,若mayInterruptIfRunning設定為false,則返回false;如果任務還沒有執行,則無論mayInterruptIfRunning為true還是false,肯定返回true。
isCancelled方法表示任務是否被取消成功,如果在任務正常完成前被取消成功,則返回 true。
isDone方法表示任務是否已經完成,若任務完成,則返回true;
get()方法用來獲取執行結果,這個方法會產生阻塞,會一直等到任務執行完畢才返回;
get(long timeout, TimeUnit unit)用來獲取執行結果,如果在指定時間內,還沒獲取到結果,就直接返回null。
之後RunnableFuture介面繼承了Runnable介面和Future介面,重點,繼承了兩個介面,然後FutureTask類是RunnableFuture的實現類。
FutureTask的構造方法有兩個,一個接受Callable物件,另一個接受runnable物件,但是接受了runnable物件之後會呼叫Executors.callable()方法,將這個runnable物件轉化成callable物件,具體的轉化就是通過一個RunnableAdapter類生成一個callabled物件,然後這個callable物件的call方法就是runnable的run方法。那就不用想也明白了,FutureTask中的實現的run方法一定是執行的直接傳進來的callable物件或者轉化來的call able物件的call方法。事實也是這樣。
那這個callable到底是什麼,怎麼這麼牛逼啊,runnable傳進來還要轉化成他。
public interface Callable<V> {
V call() throws Exception;
}
因為call方法執行完可以返回引數,就這麼簡單,而run的返回是void。ok,正點來了,當我們起一個執行緒去做一個任務的時候呼叫run方法是沒有返回值的,如果我們需要的話就只能用callable了。因為call方法是可以返回結果的。
回頭再看submit方法,傳入一個runnable或者callable,開啟一個新的執行緒去執行,然後返回一個future物件,用來對任務進行操作,是不是很牛逼的設計。
這裡上一個例子看看,這個例子就是用執行緒池加上future來實現多執行緒的一個方式。
public class FutureDemo {
//建立一個容量為1的執行緒池
static ExecutorService executorService = Executors.newFixedThreadPool(1);
public static void main(String[] args) throws Exception {
//建立執行緒並提交執行緒,同時獲取一個future物件
Thread subThread = new Thread(new SubThread());
Future future = executorService.submit(subThread);
//主執行緒處理其他工作,讓子執行緒非同步去執行
mainWork();
//阻塞,等待子執行緒結束
future.get();
System.out.println("Now all thread done!");
//關閉執行緒池
executorService.shutdown();
}
//主執行緒工作
private static void mainWork(){
System.out.println("Main thread start work!");
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Main Thread work done!");
}
/**
* 子執行緒類
* @author fuhg
*/
private static class SubThread implements Runnable{
public void run() {
// TODO Auto-generated method stub
System.out.println("Sub thread is starting!");
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Sub thread is stopping!");
}
}
}
當然如果要用future實現多執行緒並不是一定要用執行緒池,只是ExecutorService中的submit可以直接返回future物件,如果我們自己定義future物件的話,就可以不用執行緒池來實現。Callable<Chuju> onlineShopping = new Callable<Chuju>() { @Override public Chuju call() throws Exception { System.out.println("第一步:下單"); System.out.println("第一步:等待送貨"); Thread.sleep(5000); // 模擬送貨時間 System.out.println("第一步:快遞送到"); return new Chuju(); } }; FutureTask<Chuju> task = new FutureTask<Chuju>(onlineShopping); new Thread(task).start(); 這裡我們自己定義了futuretask物件,然後直接作為引數傳遞給thread並執行也是ok的,對任務的操作直接用我們自己定義的task就行了。
Callable<String> callable=new Callable<String>() {
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
System.out.println("start call");
Thread.sleep(5000);
System.out.println("end call");
return "hello word";
}
};
ExecutorService executorService = Executors.newFixedThreadPool(1);
//建立執行緒並提交執行緒,同時獲取一個future物件
Future future = executorService.submit(callable);
//主執行緒處理其他工作,讓子執行緒非同步去執行
//mainWork();
//阻塞,等待子執行緒結束
try {
System.out.println(future.get());//獲取子執行緒執行結束之後返回的結果
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Now all thread done!");//如果呼叫了get但是子執行緒還沒有執行結束,那麼主執行緒就會阻塞,那麼這句話要在子執行緒
//執行結束之後,主執行緒開始執行才能輸出。
futuretask有一個重要的屬性就是state
private volatile int state; // 注意volatile關鍵字
/**
* 在構建FutureTask時設定,同時也表示內部成員callable已成功賦值,
* 一直到worker thread完成FutureTask中的run();
*/
private static final int NEW = 0;
/**
* woker thread在處理task時設定的中間狀態,處於該狀態時,
* 說明worker thread正準備設定result.
*/
private static final int COMPLETING = 1;
/**
* 當設定result結果完成後,FutureTask處於該狀態,代表過程結果,
* 該狀態為最終狀態final state,(正確完成的最終狀態)
*/
private static final int NORMAL = 2;
/**
* 同上,只不過task執行過程出現異常,此時結果設值為exception,
* 也是final state
*/
private static final int EXCEPTIONAL = 3;
/**
* final state, 表明task被cancel(task還沒有執行就被cancel的狀態).
*/
private static final int CANCELLED = 4;
/**
* 中間狀態,task執行過程中被interrupt時,設定的中間狀態
*/
private static final int INTERRUPTING = 5;
/**
* final state, 中斷完畢的最終狀態,幾種情況,下面具體分析
*/
private static final int INTERRUPTED = 6;
然後還給出了四種可能的結果
Possible state transitions:
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
futuretask的cancel方法,get方法都會應用到這些狀態。其中get方法會對呼叫執行緒進行阻塞。
暫時就這麼多把,太深層次的我也看不懂了。