ExecutorService 使用記錄介紹

一 Java通過Executors提供四種線程池,分別爲:

newCachedThreadPool創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。 
newFixedThreadPool 創建一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。 
newScheduledThreadPool 創建一個定長線程池,支持定時及週期性任務執行。 
newSingleThreadExecutor 創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。

二、 ExecutorService 的submit() 與execute()區別 
1、接收的參數不一樣 submit()可以接受runnable無返回值和callable有返回值 
execute()接受runnable 無返回值

2、submit有返回值,而execute沒有

Method submit extends base method Executor.execute by creating and returning a Future that can be used to cancel execution and/or wait for completion.

用到返回值的例子,比如說我有很多個做validation的task,我希望所有的task執行完,然後每個task告訴我它的執行結果,是成功還是失敗,如果是失敗,原因是什麼。

3、submit方便Exception處理

There is a difference when looking at exception handling. If your tasks throws an exception and if it was submitted with execute this exception will go to the uncaught exception handler (when you don’t have provided one explicitly, the default one will just print the stack trace to System.err). If you submitted the task with submit any thrown exception, checked or not, is then part of the task’s return status. For a task that was submitted with submit and that terminates with an exception, the Future.get will rethrow this exception, wrapped in an ExecutionException.意思就是如果你在你的task裏會拋出checked或者unchecked exception,而你又希望外面的調用者能夠感知這些exception並做出及時的處理,那麼就需要用到submit,通過捕獲Future.get拋出的異常。



import java.util.ArrayList;

import java.util.List;

import java.util.Random;

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;


public class ExecutorServiceTest {

public static void main(String[] args) {

ExecutorService executorService = Executors.newCachedThreadPool();

List<Future<String>> resultList = new ArrayList<Future<String>>();


// 創建10個任務並執行

for (int i = 0; i < 10; i++) {

// 使用ExecutorService執行Callable類型的任務,並將結果保存在future變量中

Future<String> future = executorService.submit(new TaskWithResult(i));

// 將任務執行結果存儲到List中

resultList.add(future);

}

executorService.shutdown();


// 遍歷任務的結果

for (Future<String> fs : resultList) {

try {

System.out.println(fs.get()); // 打印各個線程(任務)執行的結果

} catch (InterruptedException e) {

e.printStackTrace();

} catch (ExecutionException e) {

executorService.shutdownNow();

e.printStackTrace();

return;

}

}

}

}


class TaskWithResult implements Callable<String> {

private int id;


public TaskWithResult(int id) {

this.id = id;

}


/**

* 任務的具體過程,一旦任務傳給ExecutorService的submit方法,則該方法自動在一個線程上執行。

*

* @return

* @throws Exception

*/

public String call() throws Exception {

System.out.println("call()方法被自動調用,幹活!!! " + Thread.currentThread().getName());

if (new Random().nextBoolean())

throw new TaskException("Meet error in task." + Thread.currentThread().getName());

// 一個模擬耗時的操作

for (int i = 999999999; i > 0; i--)

;

return "call()方法被自動調用,任務的結果是:" + id + " " + Thread.currentThread().getName();

}

}


class TaskException extends Exception {

public TaskException(String message) {

super(message);

}

}

執行的結果類似於:


call()方法被自動調用,幹活!!! pool-1-thread-1

call()方法被自動調用,幹活!!! pool-1-thread-2

call()方法被自動調用,幹活!!! pool-1-thread-3

call()方法被自動調用,幹活!!! pool-1-thread-5

call()方法被自動調用,幹活!!! pool-1-thread-7

call()方法被自動調用,幹活!!! pool-1-thread-4

call()方法被自動調用,幹活!!! pool-1-thread-6

call()方法被自動調用,幹活!!! pool-1-thread-7

call()方法被自動調用,幹活!!! pool-1-thread-5

call()方法被自動調用,幹活!!! pool-1-thread-8

call()方法被自動調用,任務的結果是:0 pool-1-thread-1

call()方法被自動調用,任務的結果是:1 pool-1-thread-2

java.util.concurrent.ExecutionException: com.cicc.pts.TaskException: Meet error in task.pool-1-thread-3

at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)

at java.util.concurrent.FutureTask.get(FutureTask.java:83)

at com.cicc.pts.ExecutorServiceTest.main(ExecutorServiceTest.java:29)

Caused by: com.cicc.pts.TaskException: Meet error in task.pool-1-thread-3

at com.cicc.pts.TaskWithResult.call(ExecutorServiceTest.java:57)

at com.cicc.pts.TaskWithResult.call(ExecutorServiceTest.java:1)

at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)

at java.util.concurrent.FutureTask.run(FutureTask.java:138)

at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)

at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)

at java.lang.Thread.run(Thread.java:619)
  1.  

可以看見一旦某個task出錯,其它的task就停止執行。

三、shotdown() showdownNow()區別

可以關閉 ExecutorService,這將導致其拒絕新任務。提供兩個方法來關閉 ExecutorService。 
shutdown() 方法在終止前允許執行以前提交的任務, 
shutdownNow() 方法阻止等待任務啓動並試圖停止當前正在執行的任務。在終止時執行程序沒有任務在執行,也沒有任務在等待執行,並且無法提交新任務。關閉未使用的 ExecutorService 以允許回收其資源。 
一般分兩個階段關閉 ExecutorService。第一階段調用 shutdown 拒絕傳入任務,然後調用 shutdownNow(如有必要)取消所有遺留的任務

四、Runnable()與Callable()區別

如果是一個多線程協作程序,比如菲波拉切數列,1,1,2,3,5,8…使用多線程來計算。 
但後者需要前者的結果,就需要用callable接口了。 
callable用法和runnable一樣,只不過調用的是call方法,該方法有一個泛型返回值類型,你可以任意指定。

runnable接口實現的沒有返回值的併發編程。 

callable實現的存在返回值的併發編程。(call的返回值String受泛型的影響) 使用Future獲取返回值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章