1. 程式人生 > >Java併發——Executor框架(一)

Java併發——Executor框架(一)

對看過的資料進行了整理,方便自己學習

來源:https://www.cnblogs.com/love-Stefanie/p/6728228.html

           https://www.cnblogs.com/MOBIN/p/5436482.html

1. Executor框架包括:

執行緒池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。

Executor框架從任務執行中解耦了任務的提交操作。

Executor:

一個介面,其定義了一個接收Runnable物件的方法executor,其方法簽名為executor(Runnable command),

ExecutorService:是一個比Executor使用更廣泛的子類介面,其提供了生命週期管理的方法,以及可跟蹤一個或多個非同步任務執行狀況返回Future的方法

AbstractExecutorService:ExecutorService執行方法的預設實現

ScheduledExecutorService:一個可定時排程任務的介面,能和Timer/TimerTask類似,解決那些需要任務重複執行的問題。

ScheduledThreadPoolExecutor:

繼承ThreadPoolExecutor的ScheduledExecutorService實現,一個可定時排程任務的執行緒池

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的侷限性。