Java併發——Executor框架(一)
對看過的資料進行了整理,方便自己學習
1. Executor框架包括:
執行緒池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。
Executor框架從任務執行中解耦了任務的提交操作。
Executor:
ExecutorService:是一個比Executor使用更廣泛的子類介面,其提供了生命週期管理的方法,以及可跟蹤一個或多個非同步任務執行狀況返回Future的方法
AbstractExecutorService:ExecutorService執行方法的預設實現
ScheduledExecutorService:一個可定時排程任務的介面,能和Timer/TimerTask類似,解決那些需要任務重複執行的問題。
ScheduledThreadPoolExecutor:
ThreadPoolExecutor:執行緒池,可以通過呼叫Executors以下靜態工廠方法來建立執行緒池並返回一個ExecutorService物件
2. 為什麼要使用Executor框架?
其內部使用了執行緒池機制。好處如下:
(1)減少執行緒建立和銷燬的次數,使執行緒可以多次複用,減少了建立執行緒所需要的開銷
(2)可以根據系統情況,調整執行緒的數量。防止建立過多的執行緒,消耗過多的記憶體(每個執行緒1M左右)
3. Executor和ExecutorService
Executor:一個介面,其定義了一個接收Runnable物件的方法executor,其方法簽名為executor(Runnable command),該方法接收一個Runable例項,它用來執行一個任務,任務即一個實現了Runnable介面的類,一般來說,Runnable任務開闢在新執行緒中的使用方法為:new Thread(new RunnableTask())).start(),但在Executor中,可以使用Executor而不用顯示地建立執行緒:executor.execute(new RunnableTask()); // 非同步執行
ExecutorService:是一個比Executor使用更廣泛的子類介面,其提供了生命週期管理的方法,返回 Future 物件,以及可跟蹤一個或多個非同步任務執行狀況返回Future的方法;可以呼叫ExecutorService的shutdown()方法來平滑地關閉 ExecutorService,呼叫該方法後,將導致ExecutorService停止接受任何新的任務且等待已經提交的任務執行完成(已經提交的任務會分兩類:一類是已經在執行的,另一類是還沒有開始執行的),當所有已經提交的任務執行完畢後將會關閉ExecutorService。因此我們一般用該介面來實現和管理多執行緒。
ExecutorService介面下的方法:
(1)execute(Runnable):從父類繼承過來的方法
public class Demo1 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor(); //建立一個單執行緒
executorService.execute(new Runnable() { //接收一個Runnable例項
public void run() {
System.out.println("Asynchronous task");
}
});
executorService.shutdown();
}
}
這個方法有個問題,就是沒有辦法獲知task的執行結果。如果我們想獲得task的執行結果,我們可以傳入一個Callable的例項
(2)submit(Runnable)方法:返回一個Future物件,通過返回的Future物件,我們可以檢查提交的任務是否執行完畢。
public class Demo2 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newSingleThreadExecutor(); //建立一個單執行緒
Future future = executorService.submit(new Runnable() { //接收一個Runnable例項
public void run() {
System.out.println("Asynchronous task");
}
});
System.out.println(future.get()); //任務執行結束返回null.
executorService.shutdown();
}
}
(3)submit(Callable):與submit(Callable)類似,也會返回一個Future物件,但是除此之外,submit(Callable)接收的是一個Callable的實現,Callable介面中的call()
方法有一個返回值,可以返回任務的執行結果,而Runnable介面中的run()
方法是void
的,沒有返回值。
public class Demo1 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newSingleThreadExecutor(); //建立一個單執行緒
Future<Object> future = executorService.submit(new Callable<Object>() { //接收一個Callable例項
public Object call() {
System.out.println("Asynchronous task");
return "Callable Result";
}
});
System.out.println("future.get()="+future.get());
executorService.shutdown();
}
}
(4)invokeAny(...):方法接收的是一個Callable的集合,執行這個方法不會返回Future,但是會返回所有Callable任務中其中一個任務的執行結果。這個方法也無法保證返回的是哪個任務的執行結果,反正是其中的某一個。
public class Demo2 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Set<Callable<String>> callables = new HashSet<Callable<String>>();
callables.add(new Callable<String>(){
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
return "Result1";
}
});
callables.add(new Callable<String>(){
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
return "Result2";
}
});
callables.add(new Callable<String>(){
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
return "Result3";
}
});
String result = executorService.invokeAny(callables);
System.out.println(result);
executorService.shutdown();
}
}
(5) invokeAll(...):與 invokeAny(...)
類似也是接收一個Callable集合,但是前者執行之後會返回一個Future的List,其中對應著每個Callable任務執行後的Future物件。
public class Demo3 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Set<Callable<String>> callables = new HashSet<Callable<String>>();
callables.add(new Callable<String>(){
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
return "Result1";
}
});
callables.add(new Callable<String>(){
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
return "Result2";
}
});
callables.add(new Callable<String>(){
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
return "Result3";
}
});
List<Future<String>> futures = executorService.invokeAll(callables); //返回一個Future的List集合
for(Future<String> future:futures){
System.out.println("future.get()="+future.get());
}
executorService.shutdown();
}
}
(6)shutdown():我們使用完成ExecutorService之後應該關閉它,否則它裡面的執行緒會一直處於執行狀態。
舉個例子,如果的應用程式是通過main()方法啟動的,在這個main()退出之後,如果應用程式中的ExecutorService沒有關閉,這個應用將一直執行。之所以會出現這種情況,是因為ExecutorService中執行的執行緒會阻止JVM關閉。
如果要關閉ExecutorService中執行的執行緒,我們可以呼叫ExecutorService.shutdown()
方法。在呼叫shutdown()方法之後,ExecutorService不會立即關閉,但是它不再接收新的任務,直到當前所有執行緒執行完成才會關閉,所有在shutdown()執行之前提交的任務都會被執行。
如果我們想立即關閉ExecutorService,我們可以呼叫ExecutorService.shutdownNow()
方法。這個動作將跳過所有正在執行的任務和被提交還沒有執行的任務。但是它並不對正在執行的任務做任何保證,有可能它們都會停止,也有可能執行完成。
4.總結Executor的侷限性
(1)Executor介面僅僅關注Runnable介面。一個可執行任務是無法簡單地向其呼叫者返回結果值的(因為Runnable的run()方法不會返回結果值);
注:java.lang.Runnable 一個可執行的任務。
java.util.concurrent.Callable 一個可被呼叫的任務。
(2)Executor也沒有提供一種方式來追蹤正在執行的任務程序、取消正在執行的任務或者確定執行的任務什麼時候結束執行;
(3)Executor無法執行一組可執行的任務;
(4)Executor也沒有為應用程式提供一種關閉executor的方式(或者說更為正確的關閉executor)
而ExecutorService介面克服了Executor的侷限性。