一、概述
- 通過java.util.concurrent.ExecutorService接口對象來執(zhí)行任務,該接口對象通過工具類java.util.concurrent.Executors的靜態(tài)方法來創(chuàng)建。
- Executors此包中所定義的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 類的工廠和實用方法。
- ExecutorService提供了管理終止的方法,以及可為跟蹤一個或多個異步任務執(zhí)行狀況而生成 Future 的方法。 可以關(guān)閉 ExecutorService,這將導致其停止接受新任務。關(guān)閉后,執(zhí)行程序?qū)⒆詈蠼K止,這時沒有任務在執(zhí)行,也沒有任務在等待執(zhí)行,并且無法提交新任務。
Java通過Executors創(chuàng)建線程池,目前有
- newCachedThreadPool:創(chuàng)建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
- newFixedThreadPool: 創(chuàng)建一個定長線程池,可控制線程最大并發(fā)數(shù),超出的線程會在隊列中等待。
- newScheduledThreadPool: 創(chuàng)建一個定長線程池,支持定時及周期性任務執(zhí)行。
- newSingleThreadExecutor: 創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務,保證所有任務按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行。
- newWorkStealingPool(jdk1.8 新增):創(chuàng)建指定并行數(shù)的線程池,使用多重隊列減少競爭,不能保證線程執(zhí)行的順序。默認并行數(shù)為最大CPU內(nèi)核數(shù)
注意:線程池只是為了控制應用中處理某項業(yè)務中防止高并發(fā)問題帶來的線程不安全的發(fā)生的概率。線程不可以重用,每次新創(chuàng)建一個線程用來處理業(yè)務。可以通過線程池指定處理這項業(yè)務最大的同步線程數(shù)。
執(zhí)行任務流程
- 創(chuàng)建ExecutorService
通過工具類java.util.concurrent.Executors的靜態(tài)方法來創(chuàng)建。 - 將任務添加到線程去執(zhí)行
當將一個任務添加到線程池中的時候,線程池會為每個任務創(chuàng)建一個線程,該線程會在之后的某個時刻自動執(zhí)行。
二、幾種線程池詳解
線程示例
public class ExecutorsThread implements Runnable {
private int index;
public ExecutorsThread(int index) {
this.index = index;
}
@Override
public void run() {
try {
System.out.println("開始處理線程!");
Thread.sleep(100 * index);
System.out.println(String.format("我是線程%s[%s]", Thread.currentThread().getName(), this.toString()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
1、newCachedThreadPool
創(chuàng)建一個可緩存線程池,應用中存在的線程數(shù)最大是 231-1
@Test
public void testNewCachedThreadPool() throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i=1; i<=10; i++) {
ExecutorsThread executorsThread = new ExecutorsThread(i);
executorService.execute(executorsThread);
}
Thread.sleep(1000*60);
}
2、newFixedThreadPool
創(chuàng)建一個定長線程池,可控制線程最大并發(fā)數(shù),超出的線程會在隊列中等待。Executors.newFixedThreadPool(3);在線程池中保持三個線程可以同時執(zhí)行,但是注意,并不是說線程池中永遠都是這三個線程,只是說可以同時存在的線程數(shù),當某個線程執(zhí)行結(jié)束后,會有新的線程進來。
newFixedThreadPool.execute(new ThreadForpools());這句話的含義并不是添加新的線程,而是添加新的處理業(yè)務請求進來。當該線程獲取CPU時間后開始執(zhí)行。
@Test
public void testNewFixedThreadPool() throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i=1; i<=10; i++) {
ExecutorsThread executorsThread = new ExecutorsThread(i);
executorService.execute(executorsThread);
}
Thread.sleep(1000*60);
}
3、newScheduledThreadPool
創(chuàng)建一個定長線程池,支持定時及周期性任務執(zhí)行。
@Test
public void testNewScheduledThreadPool() throws InterruptedException {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
for (int i=1; i<=10; i++) {
ExecutorsThread executorsThread = new ExecutorsThread(i);
// executorService.execute(executorsThread);
executorService.schedule(executorsThread, 1, TimeUnit.SECONDS);
}
Thread.sleep(1000*60);
}
4、newSingleThreadExecutor
創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務,保證所有任務按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行。
@Test
public void testNewSingleThreadExecutor() throws InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i=1; i<=10; i++) {
ExecutorsThread executorsThread = new ExecutorsThread(i);
executorService.submit(executorsThread);
}
Thread.sleep(1000*60);
}
5、newWorkStealingPool
創(chuàng)建一個指定并行數(shù)的線程池,使用多重隊列減少競爭,不能保證線程執(zhí)行的順序。活動的線程數(shù)可以動態(tài)地增長或收縮。
@Test
public void testNewWorkStealingPool() throws InterruptedException {
ExecutorService executorService = Executors.newWorkStealingPool(20);
for (int i=1; i<=100; i++) {
ExecutorsThread executorsThread = new ExecutorsThread(i);
executorService.execute(executorsThread);
}
Thread.sleep(1000*60*2);
}
三、兩種任務
在Java5之后,任務分兩類
1、實現(xiàn) Runnable 接口的類
2、實現(xiàn) Callable 接口的類
- 概述:返回結(jié)果并且可能拋出異常的任務。
- 實現(xiàn)者定義了一個不帶任何參數(shù)的叫做 call 的方法。
- Callable 接口類似于,兩者都是為那些其實例可能被另一個線程執(zhí)行的類設計的。但是 Runnable 不會返回結(jié)果,并且無法拋出經(jīng)過檢查的異常。
- 類包含一些從其他普通形式轉(zhuǎn)換成 Callable 類的實用方法。
- 任務執(zhí)行返回值Futrue<T> 可以收集返回值和執(zhí)行過程中遇到的異常信息,對于編寫高質(zhì)量的代碼很有幫助。還可以取消任務。
代碼實現(xiàn)
public class ExecutorsCallable implements Callable<String> {
private int index;
public ExecutorsCallable(int index) {
this.index = index;
}
@Override
public String call() throws Exception {
System.out.println(String.format("任務[%s] call() 方法被自動調(diào)用!", Thread.currentThread().getName()));
if (new Random().nextBoolean()) {
throw new RuntimeException(String.format("任務[%s]執(zhí)行出錯!", Thread.currentThread().getName()));
}
for (int i=10000; i>0; i--);
return String.format("任務[%s] call() 方法被自動調(diào)用,任務結(jié)果是%d!", Thread.currentThread().getName(), index);
}
}
@Test
public void testCallable() throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
List<Future<String>> resultList = new ArrayList<>();
for (int i=1; i<=10; i++) {
ExecutorsCallable callable = new ExecutorsCallable(i);
// ExecutorService 執(zhí)行 Callable 類型的任務,并將結(jié)果保存在 future 變量中
Future<String> future = executorService.submit(callable);
// 將執(zhí)行結(jié)果存儲到 list 中
resultList.add(future);
}
//啟動一次順序關(guān)閉,執(zhí)行以前提交的任務,但不接受新任務。如果已經(jīng)關(guān)閉,則調(diào)用沒有其他作用。
executorService.shutdown();
for (Future<String> future : resultList) {
try {
System.out.println(future.get());
} catch (ExecutionException e) {
e.printStackTrace();
}
}
Thread.sleep(1000*60*2);
}
實現(xiàn) Runnable 接口的類與實現(xiàn) Callable 接口的類的區(qū)別
相同點
- 都可以被ExecutorService執(zhí)行
不同點
- Runnable 任務執(zhí)行 run 方法,而 Callable 執(zhí)行 call() 方法
- Runnable 任務沒有返回值,而 Callable 任務有返回值
- Runnable 任務不拋出異常,而 Callable 可以拋出檢查的異常
- Callable 的 call() 方法只能通過 ExecutorService的 (<T> task) 方法來執(zhí)行,并且返回一個 <T><T>,是表示任務等待完成的 Future。
- 將Runnable的對象傳遞給ExecutorService的submit方法,則該run方法自動在一個線程上執(zhí)行,并且會返回執(zhí)行結(jié)果Future對象,但是在該Future對象上調(diào)用get方法,將返回null。
將一個Callable的對象傳遞給ExecutorService的submit方法,則該call方法自動在一個線程上執(zhí)行,并且會返回執(zhí)行結(jié)果Future對象。
submit與execute的區(qū)別
- 接收的參數(shù)不一樣,submit 可以接收 Runnable 或 Callable 接口參數(shù),execute 只能接收 Runnable 接口參數(shù)。
- submit 有返回值,execute 沒有。
- submit 可以對 Exception 處理
結(jié)束語
1.清楚Runnable 和 Callable 任務的創(chuàng)建方式
2.ExecutorService 創(chuàng)建線程池的幾種方式與區(qū)別
3.ExecutorService submit 和 execute 的區(qū)別
4.Future<T> 的合理利用
參考資料
https://www.cnblogs.com/ljp-sun/p/6580147.html
https://www.cnblogs.com/wanqieddy/p/3853863.html