一、前期知識(shí)概要
1、設(shè)計(jì)模式對(duì)象池(資源池)
在我們的日常生活我們聽過(guò)水池,電池等等,水池了用來(lái)存放水,電池用來(lái)存放電,而在編程的世界中的池是用來(lái)存放一組資源
資源池(Resource pool)也叫對(duì)象池(Object pool) 被認(rèn)為是一種設(shè)計(jì)模式,這里的資源主要是指系統(tǒng)資源, 這些資源不專屬于某個(gè)進(jìn)程或內(nèi)部資源。客戶端向池請(qǐng)求資源, 并使用返回的資源進(jìn)行指定的操作。當(dāng)客戶端使用完資源后, 會(huì)把資源放回池中而不是釋放或丟棄掉。
總結(jié)一句話: 需要時(shí),從池中提取,不用時(shí),放回池中
舉個(gè)栗子: 對(duì)象池就想我們公司的倉(cāng)庫(kù),比如我們?nèi)ス旧习?公司會(huì)給我們提供一個(gè)工位,行政人員會(huì)給我們提供相應(yīng)的辦公設(shè)備,那這個(gè)時(shí)候她首先會(huì)看一下庫(kù)房中,如果庫(kù)房中有,直接從庫(kù)房中拿,如果庫(kù)房中沒有,那就會(huì)去網(wǎng)上或者商店購(gòu)買一個(gè)新的。如果員工離職了正常情況下會(huì)將員工的能用的辦公物品放到庫(kù)房。
image
2、應(yīng)用場(chǎng)景
它用在當(dāng)對(duì)象的初始化過(guò)程代價(jià)較大或者使用頻率較高時(shí),比如線程池,數(shù)據(jù)庫(kù)連接池等。運(yùn)用對(duì)象池化技術(shù)可以顯著地提升性能。
二、為什么要使用
創(chuàng)建線程對(duì)象不像其他對(duì)象一樣在JVM分配內(nèi)存即可,還要調(diào)用操作系統(tǒng)內(nèi)核的API,然后操作系統(tǒng)為線程分配一系列的資源,這個(gè)成本就很高了。所以線程是一個(gè)重量級(jí)對(duì)象,應(yīng)該避免頻繁創(chuàng)建和銷毀
降低資源消耗。 通過(guò)重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。
提高響應(yīng)速度。 當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要的等到線程創(chuàng)建就能立即執(zhí)行。
提高線程的可管理性。 線程是稀缺資源,如果無(wú)限制的創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。
引用《Java并發(fā)編程的藝術(shù)》
三、Java線程池的架構(gòu)設(shè)計(jì)
1、說(shuō)明
Java里面線程池的頂級(jí)接口是Executor,該類位于
java.util.concurrent,但是嚴(yán)格意義上講Executor并不是一個(gè)線程池,而只是一個(gè)執(zhí)行線程的工具。真正的線程池接口是ExecutorService。
2、重要類說(shuō)明
| 類或者接口 | 說(shuō)明 |
|---|---|
| ExecutorService | 真正的線程池接口。 |
| ScheduledExecutorService | 定時(shí)任務(wù)與線程池功能結(jié)合使用 |
| ThreadPoolExecutor | ExecutorService的默認(rèn)實(shí)現(xiàn)。重點(diǎn) |
| ScheduledThreadPoolExecutor | 周期性任務(wù)調(diào)度。 |
3、結(jié)構(gòu)圖


4、Executor
- 說(shuō)明
Executor接口只有一個(gè)execute方法,執(zhí)行提交Runnable任務(wù),用來(lái)替代通常啟動(dòng)線程的方法 - 方法
execute(Runnable r) - 舉個(gè)栗子
/*以前*/ Thread t = new Thread(); t.start(); /*使用線程池*/ Thread t = new Thread(); executor.execute(t)
5、ExecutorService
- 說(shuō)明
ExecutorService接口繼承自Executor接口,真正的線程池核心類。提供了管理終止的方法,以及可為跟蹤一個(gè)或多個(gè)異步任務(wù)執(zhí)行狀況而生成 Future 的方法。增加了shutDown(),shutDownNow(),invokeAll(),invokeAny()和submit()等方法。如果需要支持即時(shí)關(guān)閉,也就是shutDownNow()方法,則任務(wù)需要正確處理中斷。 - 核心方法
方法名 返回值 說(shuō)明 **submit(Callable task) ** Future<T>提交一個(gè)可運(yùn)行的任務(wù)執(zhí)行,并返回一個(gè)表示該任務(wù)結(jié)果 submit(Runable task) Future<T>提交一個(gè)可運(yùn)行的任務(wù)執(zhí)行,并返回一個(gè)表示該任務(wù)結(jié)果 shutdown() 布爾 阻止新來(lái)的任務(wù)提交,對(duì)已經(jīng)提交了的任務(wù)不會(huì)產(chǎn)生任何影響。當(dāng)已經(jīng)提交的任務(wù)執(zhí)行完后,它會(huì)將那些閑置的線程進(jìn)行中斷,這個(gè)過(guò)程是異步的 shutdownNow() List<Runable>設(shè)置線程池的狀態(tài)為STOP,然后嘗試停止所有的正在執(zhí)行或暫停任務(wù)的線程,并返回等待執(zhí)行任務(wù)的列表 isShutdown() 布爾 檢測(cè)線程池是否正處于關(guān)閉中 isTerminated() 布爾 所有任務(wù)在關(guān)閉后完成,則返回 true。awaitTermination() 布爾 定時(shí)或者永久等待線程池關(guān)閉結(jié)束 - 舉個(gè)栗子
private static int TASK_COUNT = 10; public static void main(String[] args) { /*1. 創(chuàng)建線程池對(duì)象 */ ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 8, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5)); /*2. 提交任務(wù)*/ for (int i = 0; i < TASK_COUNT; i++) { pool.execute(() -> { System.out.println(Thread.currentThread().getName() + ":----->在執(zhí)行任務(wù)"); }); } /*3. 關(guān)閉連接池*/ pool.shutdown(); /* 或者 */ // pool.shutdownNow() }
6、ScheduledExecutorService
- 說(shuō)明
ScheduledExecutorService是基于線程池設(shè)計(jì)的定時(shí)任務(wù)類,每個(gè)調(diào)度任務(wù)都會(huì)分配到線程池中的一個(gè)線程去執(zhí)行,任務(wù)是并發(fā)執(zhí)行,互不影響。 -
關(guān)系圖
image - 示例代碼
public static void main(String[] args) { // 創(chuàng)建定時(shí)任務(wù)線程池 ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); //設(shè)置日期格式 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 提交一個(gè)任務(wù)兩秒之后開始執(zhí)行 executorService.schedule(() -> { System.out.println("++++++++++++++++++++子線程:" + df.format(new Date())); }, 2, TimeUnit.SECONDS); System.out.println("主線程: " + df.format(new Date())); // executorService.shutdown(); }
7、工作流程(了解)

四、線程池的狀態(tài)與生命周期
1、概要
線程池有運(yùn)行、關(guān)閉、停止、清空狀態(tài)、結(jié)束五種狀態(tài),結(jié)束后就會(huì)釋放所有資源
- RUNNING(運(yùn)行): 接受新的任務(wù)和處理隊(duì)列中的任務(wù)
- SHUTDOWN(關(guān)閉): 不接受新的請(qǐng)求,但會(huì)處理已經(jīng)添加到隊(duì)列中的任務(wù)
- STOP(停止): 不接收新任務(wù),也不處理隊(duì)列任務(wù),并且中斷所有處理中的任務(wù)。
- TIDYING(整理):所有任務(wù)都被終結(jié),有效線程為0。會(huì)觸發(fā)terminated()方法
- TERMINATED(結(jié)束):當(dāng)terminated()方法執(zhí)行結(jié)束
2、流程圖

五、ThreadPoolExecutor
1、線程池的創(chuàng)建
- 構(gòu)造方法
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
2、參數(shù)概要
| 參數(shù) | 概要 |
|---|---|
| corePoolSize | 池中所保存的線程數(shù),包括空閑線程。 |
| maximumPoolSize | 池中允許的最大線程數(shù)。 |
| keepAliveTime | 當(dāng)線程數(shù)大于核心時(shí),此為終止前多余的空閑線程等待新任務(wù)的最長(zhǎng)時(shí)間。 |
| unit - keepAliveTime | 時(shí)間單位。 |
| workQueue | 執(zhí)行前用于保持任務(wù)的隊(duì)列。此隊(duì)列僅保持由 execute方法提交的 Runnable任務(wù)。 |
| threadFactory | 執(zhí)行程序創(chuàng)建新線程時(shí)使用的工廠 |
| handler | 由于超出線程范圍和隊(duì)列容量而使執(zhí)行被阻塞時(shí)所使用的處理程序。 |
3、參數(shù)詳解
3.1、corePoolSize(必要參數(shù))
核心線程數(shù)。默認(rèn)情況下,核心線程會(huì)一直存活,但是當(dāng)將allowCoreThreadTimeout設(shè)置為true時(shí),核心線程也會(huì)超時(shí)回收。
3.2、maximumPoolSize(必要參數(shù))
線程池允許創(chuàng)建的最大線程數(shù)。如果隊(duì)列滿了,并且已創(chuàng)建的線程數(shù)小于最大線程數(shù),則線程池會(huì)再創(chuàng)建新的線程執(zhí)行任務(wù)。值得注意的是如果使用了無(wú)界的任務(wù)隊(duì)列這個(gè)參數(shù)就沒什么效果。
3.3、keepAliveTime(必要參數(shù))
線程池的工作線程空閑后,保持存活的時(shí)間。所以如果任務(wù)很多,并且每個(gè)任務(wù)執(zhí)行的時(shí)間比較短,可以調(diào)大這個(gè)時(shí)間,提高線程的利用率。
3.4、unit(必要參數(shù))
指定keepAliveTime參數(shù)的時(shí)間單位
可選值 說(shuō)明 TimeUnit.DAYS 天 TimeUnit.HOURS 小時(shí) TimeUnit.MINUTES(常用) 分鐘 TimeUnit.SECONDS(常用) 秒 TimeUnit.MILLISECONDS(常用) 毫秒 TimeUnit.MICROSECONDS 微秒(千分之一毫秒) TimeUnit.NANOSECONDS 毫微秒(千分之一微秒)
3.5、workQueue
任務(wù)隊(duì)列。Runnable對(duì)象就存儲(chǔ)在該參數(shù)中
3.6、threadFactory(可選)
線程工廠。用于指定為線程池創(chuàng)建新線程的方式
3.7、handler(可選)
- 說(shuō)明
拒絕策略。有兩種情況會(huì)觸發(fā)拒絕策略- 隊(duì)列和線程池都滿了,說(shuō)明線程池處于飽和狀態(tài),那么必須采取一種策略處理提交的新任務(wù)。這個(gè)策略默認(rèn)情況下是 AbortPolicy,表示無(wú)法處理新任務(wù)時(shí)拋出異常
- 當(dāng)線程池被調(diào)用shutdown()后
- 可選值
策略 說(shuō)明 AbortPolicy 直接拋出異常。默認(rèn)值 CallerRunsPolicy 只用調(diào)用者所在線程來(lái)運(yùn)行任務(wù)。 DiscardOldestPolicy 丟棄隊(duì)列里最近的一個(gè)任務(wù),并執(zhí)行當(dāng)前任務(wù)。 DiscardPolicy 不處理,丟棄掉。
4、舉個(gè)栗子
- 有返回值
public static void start() { /* * 創(chuàng)建線程池,并發(fā)量最大為5 * LinkedBlockingDeque,表示執(zhí)行任務(wù)或者放入隊(duì)列 */ ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 0, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); // 存儲(chǔ)線程的返回值 List<Future<String>> results = new LinkedList<>(); for (int i = 0; i < 10; i++) { // 調(diào)用submit可以獲得線程的返回值 int num = i; Future<String> result = executor.submit(() -> num + ""); results.add(result); } //如果不調(diào)用,awaitTermination將一直阻塞 executor.shutdown(); //1天,模擬永遠(yuǎn)等待 try { System.out.println(executor.awaitTermination(1, TimeUnit.DAYS)); } catch (InterruptedException e) { e.printStackTrace(); } //輸出結(jié)果 for (int i = 0; i < 10; i++) { try { System.out.println(results.get(i).get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } }
5、corePoolSize、workQueue 、maximumPoolSize的關(guān)系
- 默認(rèn)情況下,線程池在初始的時(shí)候,線程數(shù)為0。當(dāng)接收到一個(gè)任務(wù)時(shí),如果線程池中存活的線程數(shù)小于corePoolSize核心線程,則新建一個(gè)線程。
- 如果所有運(yùn)行的核心線程都都在忙,超出核心線程處理的任務(wù),執(zhí)行器更多地選擇把任務(wù)放進(jìn)隊(duì)列,而不是新建一個(gè)線程。
- 如果一個(gè)任務(wù)提交不了到隊(duì)列,在不超出最大線程數(shù)量情況下,會(huì)新建線程。就根據(jù)指定的拒絕策略來(lái)處理,默認(rèn)拋出異常。
- 如線程閑置時(shí),線程池會(huì)根據(jù)keepAliveTime設(shè)置的時(shí)間回收大于corePoolSize的線程
六、ScheduledThreadPoolExecutor
1、簡(jiǎn)介
ScheduledThreadPoolExecutor用來(lái)執(zhí)行周期性任務(wù)的調(diào)度。在這之前的實(shí)現(xiàn)需要依靠Timer和TimerTask或者其它第三方工具來(lái)完成。它主要有以下兩個(gè)作用
- 延時(shí)執(zhí)行任務(wù)。
- 周期性重復(fù)執(zhí)行任務(wù)。
Timer ScheduledThreadPoolExecutor 單線程 多線程 單個(gè)任務(wù)執(zhí)行時(shí)間影響其他任務(wù)調(diào)度 多線程,不會(huì)影響 基于絕對(duì)時(shí)間 基于相對(duì)時(shí)間 一旦執(zhí)行任務(wù)出現(xiàn)異常不會(huì)捕獲,其他任務(wù)得不到執(zhí)行 多線程,單個(gè)任務(wù)的執(zhí)行不會(huì)影響其他線程
2、示例代碼
- 執(zhí)行一次
private static final int TASK_COUNT = 3;
public static void main(String[] args) throws InterruptedException {
// 創(chuàng)建大小為2的線程池
ScheduledExecutorService scheduledThreadPool = new ScheduledThreadPoolExecutor(2);
for (int i = 0; i < TASK_COUNT; i++) {
// 只執(zhí)行一次
scheduledThreadPool.schedule(() -> {
try {
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 5, TimeUnit.SECONDS);
}
// 關(guān)閉線程池
scheduledThreadPool.shutdown();
boolean isStop;
// 等待線程池終止
do {
isDone = scheduledThreadPool.awaitTermination(1, TimeUnit.HOURS);
System.out.println("等待任務(wù)結(jié)束中...");
} while (!isStop);
System.out.println("所有工作完成!!! 線程池關(guān)閉");
}
- 周期執(zhí)行任務(wù)
private static final int TASK_COUNT = 3; public static void main(String[] args) throws InterruptedException { // 1. 創(chuàng)建大小為2的線程池 ScheduledExecutorService scheduledThreadPool = new ScheduledThreadPoolExecutor(2); // 2. 周期性執(zhí)行,每2秒執(zhí)行一次 scheduledThreadPool.scheduleAtFixedRate(() -> { try { TimeUnit.SECONDS.sleep(1); System.out.println(Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } }, 0, 2, TimeUnit.SECONDS); } // 3.關(guān)閉線程池 scheduledThreadPool.shutdown(); boolean isStop; // 等待線程池終止 do { isStop = scheduledThreadPool.awaitTermination(1, TimeUnit.HOURS); System.out.println("等待任務(wù)結(jié)束中..."); } while (!isStop); System.out.println("所有工作完成!!! 線程池關(guān)閉"); }
七、任務(wù)隊(duì)列
1、名詞解釋
1.1、什么叫有界
有界就是有固定大小的隊(duì)列,無(wú)界表示無(wú)上限
1.2、什么叫隊(duì)列
Queue 一個(gè)隊(duì)列就是一個(gè)先入先出(FIFO)的數(shù)據(jù)結(jié)構(gòu)
Queue接口與List、Set同一級(jí)別,都是繼承了Collection接口。
2、常用隊(duì)列
2.1、ArrayBlockingQueue
- 作用
采用數(shù)組實(shí)現(xiàn)的有界阻塞線程安全隊(duì)列。如果向已滿的隊(duì)列繼續(xù)塞入元素,將導(dǎo)致當(dāng)前的線程阻塞。如果向空隊(duì)列獲取元素,那么將導(dǎo)致當(dāng)前線程阻塞。 - 構(gòu)造方法
構(gòu)造方法 參數(shù)說(shuō)明 public ArrayBlockingQueue(int capacity) 構(gòu)造指定大小的有界隊(duì)列 public ArrayBlockingQueue(int capacity, boolean fair) 構(gòu)造指定大小的有界隊(duì)列,指定為公平或非公平鎖 public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c) 構(gòu)造指定大小的有界隊(duì)列,指定為公平或非公平鎖,指定在初始化時(shí)加入一個(gè)集合 - 示例代碼
public class ArrayBlockingQueueExample { public static final int COUNT = 100; public static void main(String[] args) throws Exception { ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(10); ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 8, 60, TimeUnit.SECONDS, queue); for (int i = 0; i < COUNT; i++) { TimeUnit.SECONDS.sleep(1); executor.execute(() -> System.out.println("線程池---數(shù)組實(shí)現(xiàn)的有界阻塞線程安全隊(duì)列" + Thread.currentThread().getName())); } executor.shutdown(); } }
2.2、LinkedBlockingQueue
- 作用
一個(gè)由鏈表結(jié)構(gòu)組成的有界阻塞隊(duì)列(也可以當(dāng)無(wú)界阻塞隊(duì)列)。此隊(duì)列按 FIFO(先進(jìn)先出)原則。Executor.newFixedThreadPool()默認(rèn)隊(duì)列 - 構(gòu)造方法
構(gòu)造方法 參數(shù)說(shuō)明 public LinkedBlockingQueue() 在未指明容量時(shí),容量默認(rèn)為Integer.MAX_VALUE public LinkedBlockingQueue(int capacity) 構(gòu)造指定大小的有界隊(duì)列
2.3、SynchronousQueue;
- 作用
一個(gè)不存儲(chǔ)元素的有界阻塞隊(duì)列。每個(gè)插入操作必須等到另一個(gè)線程調(diào)用移除操作,否則插入操作一直處于阻塞狀態(tài),吞吐量通常要高于LinkedBlockingQueue,靜態(tài)工廠方法Executors.newCachedThreadPool()使用了這個(gè)隊(duì)列。 - 構(gòu)造方法
構(gòu)造方法 參數(shù)說(shuō)明 public SynchronousQueue() 默認(rèn)情況下不保證排序, public SynchronousQueue(boolean fair) 如果設(shè)置true隊(duì)列可保證線程以 FIFO 的順序進(jìn)行訪問(wèn) - 示例代碼
public class SynchronousQueueExample { public static final int COUNT = 100; public static void main(String[] args) { SynchronousQueue<Runnable> queue = new SynchronousQueue<>(); ThreadPoolExecutor executor = new ThreadPoolExecutor(2, Integer.MAX_VALUE, 1, TimeUnit.SECONDS, queue); for (int i = 0; i < COUNT; i++) { executor.execute(() -> System.out.println("線程池---同步隊(duì)列" + Thread.currentThread().getName())); } executor.shutdown(); } } - 分析
- 假設(shè)當(dāng)前有2個(gè)核心線程
- 此時(shí)來(lái)了一個(gè)任務(wù)(A),根據(jù)前面介紹的“如果運(yùn)行的線程等于或多于 corePoolSize,則 Executor 始終首選將請(qǐng)求加入隊(duì)列,而不添加新的線程?!?所以A被添加到queue中。
- 又來(lái)了一個(gè)任務(wù)(B),且核心2個(gè)線程還沒有忙完。接下來(lái)首先嘗試1中描述,但是由于使用的SynchronousQueue,所以一定無(wú)法加入進(jìn)去。
- 此時(shí)便滿足了上面提到的“如果無(wú)法將請(qǐng)求加入隊(duì)列,則創(chuàng)建新的線程”,所以必然會(huì)新建一個(gè)線程來(lái)運(yùn)行這個(gè)任務(wù)。
- 但是如果這三個(gè)任務(wù)都還沒完成,繼續(xù)來(lái)了一個(gè)任務(wù),queue中無(wú)法插入(任務(wù)A還在queue中),而線程數(shù)達(dá)到了maximumPoolSize,所以只好執(zhí)行異常策略了。
為了避免這種情況:,所以在使用SynchronousQueue通常要求maximumPoolSize是無(wú)界的(如果希望限制就直接使用有界隊(duì)列)。對(duì)于使用SynchronousQueue的作用jdk中寫的很清楚:此策略可以避免在處理可能具有內(nèi)部依賴性的請(qǐng)求集時(shí)出現(xiàn)鎖。
2.4、PriorityBlockingQueue
- 作用
一種優(yōu)先級(jí)隊(duì)列,元素并不是以FIFO的方式出/入隊(duì)。默認(rèn)大小為11,不可以插入 null 值。當(dāng)隊(duì)列滿的時(shí)候會(huì)進(jìn)行擴(kuò)容,是真正意義上的無(wú)界(僅受內(nèi)存大小限制),它不像ArrayBlockingQueue那樣構(gòu)造時(shí)必須指定最大容量,也不像LinkedBlockingQueue默認(rèn)最大容量為Integer.MAX_VALUE; - 構(gòu)造方法
構(gòu)造方法 參數(shù)說(shuō)明 PriorityBlockingQueue() PriorityBlockingQueue(int initialCapacity) 指定初始化隊(duì)列長(zhǎng)度 PriorityBlockingQueue(int initialCapacity, Comparator<? super E> comparator) 指定初始化隊(duì)列長(zhǎng)度,自定義比較器
八、Executors(了解)
1、說(shuō)明
對(duì)于新手來(lái)說(shuō)要配置一個(gè)線程池還是比較有難度的,尤其是對(duì)于線程池的原理不是很清楚的情況下,很有可能配置的線程池不是較優(yōu)的,因此在Executors類里面提供了一些靜態(tài)工廠,生成一些常用的線程池
2、注意注意注意

3、Executors靜態(tài)方法
1、newSingleThreadExecutor
- 作用
創(chuàng)建一個(gè)單線程的線程池。這個(gè)線程池只有一個(gè)線程在工作,也就是相當(dāng)于單線程串行執(zhí)行所有任務(wù)。如果這個(gè)唯一的線程因?yàn)楫惓=Y(jié)束,那么會(huì)有一個(gè)新的線程來(lái)替代它。 - 方法
Executors.newSingleThreadExecutor() - 應(yīng)用場(chǎng)景
保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行
不適合并發(fā)但可能引起IO阻塞性及影響UI線程響應(yīng)的操作,如數(shù)據(jù)庫(kù)操作、文件操作等
2、newFixedThreadExecutor
- 作用
創(chuàng)建固定大小的線程池。每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程,直到線程達(dá)到線程池的最大大小。線程池的大小一旦達(dá)到最大值就會(huì)保持不變,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束,那么線程池會(huì)補(bǔ)充一個(gè)新線程 - 方法
Executors.newFixedThreadExecutor() - 應(yīng)用場(chǎng)景
控制線程最大并發(fā)數(shù) - 舉個(gè)栗子
public static void main(String[] args) throws IOException, InterruptedException { // 創(chuàng)建一個(gè)固定大小的線程池 ExecutorService service = Executors.newFixedThreadPool(3); for (int i = 0; i < 10; i++) { System.out.println("創(chuàng)建線程" + i); Runnable run = new Runnable() { @Override public void run() { System.out.println("啟動(dòng)線程"); } }; // 在未來(lái)某個(gè)時(shí)間執(zhí)行給定的命令 service.execute(run); } // 關(guān)閉啟動(dòng)線程 service.shutdown(); // 等待子線程結(jié)束,再繼續(xù)執(zhí)行下面的代碼 service.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); System.out.println("等待所有線程執(zhí)行完成"); } } - 注意
newFixedThreadPool線程池的線程是不會(huì)釋放的,即使它是閑置的。這就會(huì)產(chǎn)生性能問(wèn)題
3、newCacheThreadExecutor
- 作用
創(chuàng)建一個(gè)可緩存的線程池。如果線程池的大小超過(guò)了處理任務(wù)所需要的線程,
那么就會(huì)回收部分空閑(60秒不執(zhí)行任務(wù))的線程,當(dāng)任務(wù)數(shù)增加時(shí),此線程池又可以智能的添加新線程來(lái)處理任務(wù)。此線程池不會(huì)對(duì)線程池大小做限制,線程池大小完全依賴于操作系統(tǒng)(或者說(shuō)JVM)能夠創(chuàng)建的最大線程大小 - 方法
Executors.newCacheThreadExecutor() - 應(yīng)用場(chǎng)景
適合執(zhí)行大量、耗時(shí)少的任務(wù) - 舉個(gè)栗子
public class ThreadPoolCached { public static void main(String[] args) { ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 100; i++) { final int index = i; try { Thread.sleep(index * 100); } catch (Exception e) { e.printStackTrace(); } cachedThreadPool.execute(new Runnable() { @Override public void run() { System.out.println("當(dāng)前線程"+Thread.currentThread().getName()); } }); } }
4、newScheduleThreadExecutor
- 作用
創(chuàng)建一個(gè)大小無(wú)限的線程池。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求 - 方法
Executors.newScheduleThreadExecutor() - 示例代碼
// 1. 創(chuàng)建 定時(shí)線程池對(duì)象 & 設(shè)置線程池線程數(shù)量固定為5 ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); // 2. 創(chuàng)建好Runnable類線程對(duì)象 & 需執(zhí)行的任務(wù) Runnable task =new Runnable(){ public void run() { System.out.println("執(zhí)行任務(wù)啦"); } }; // 3. 向線程池提交任務(wù) scheduledThreadPool.schedule(task, 1, TimeUnit.SECONDS); // 延遲1s后執(zhí)行任務(wù) scheduledThreadPool.scheduleAtFixedRate(task,10,1000,TimeUnit.MILLISECONDS);// 延遲10ms后、每隔1000ms執(zhí)行任務(wù)
九、面試題
1、shutdown
- shutdown()有什么作用?
阻止新來(lái)的任務(wù)提交,對(duì)已經(jīng)提交的任務(wù)不會(huì)產(chǎn)生任何影響 當(dāng)已經(jīng)提交的任務(wù)執(zhí)行完成之后,那些閑置的線程會(huì)被回收
這個(gè)過(guò)程是異步的。 - 如何阻止新來(lái)的任務(wù)提交?
通過(guò)將線程池的狀態(tài)改成SHUTDOWN,當(dāng)再將執(zhí)行execute提交任務(wù)時(shí),如果測(cè)試到狀態(tài)不為RUNNING,則拋出rejectedExecution,從而達(dá)到阻止新任務(wù)提交的目的。 - 為何對(duì)提交的任務(wù)不產(chǎn)生任何影響?
在調(diào)用中斷任務(wù)的方法時(shí),它會(huì)檢測(cè)workers中的任務(wù),如果worker對(duì)應(yīng)的任務(wù)沒有中斷,并且是空閑線程,它才會(huì)去中斷。另外的話,workQueue中的值,還是按照一定的邏輯順序不斷的往works中進(jìn)行輸送的,這樣一來(lái),就可以保證提交的任務(wù)按照線程本身的邏輯執(zhí)行,不受到影響。
2、shutdownNow
- shutdownNow()有什么功能?
阻止新來(lái)的任務(wù)提交,同時(shí)會(huì)中斷當(dāng)前正在運(yùn)行的線程,即workers中的線程。另外它還將workQueue中的任務(wù)給移除,并將這些任務(wù)添加到列表中進(jìn)行返回。 - 如何阻止新來(lái)的任務(wù)提交?
通過(guò)將線程池的狀態(tài)改成STOP,當(dāng)再將執(zhí)行execute提交任務(wù)時(shí),如果測(cè)試到狀態(tài)不為RUNNING,則拋出rejectedExecution,從而達(dá)到阻止新任務(wù)提交的目的. - 如果我提交的任務(wù)代碼塊中,正在等待某個(gè)資源,而這個(gè)資源沒到,但此時(shí)執(zhí)行shutdownNow(),會(huì)出現(xiàn)什么情況?
當(dāng)執(zhí)行shutdownNow()方法時(shí),如遇已經(jīng)激活的任務(wù),并且處于阻塞狀態(tài)時(shí),shutdownNow()會(huì)執(zhí)行1次中斷阻塞的操作,此時(shí)對(duì)應(yīng)的線程報(bào)InterruptedException,如果后續(xù)還要等待某個(gè)資源,則按正常邏輯等待某個(gè)資源的到達(dá)。例如,一個(gè)線程正在sleep狀態(tài)中,此時(shí)執(zhí)行shutdownNow(),它向該線程發(fā)起interrupt()請(qǐng)求,而sleep()方法遇到有interrupt()請(qǐng)求時(shí),會(huì)拋出InterruptedException(),并繼續(xù)往下執(zhí)行。在這里要提醒注意的是,在激活的任務(wù)中,如果有多個(gè)sleep(),該方法只會(huì)中斷第一個(gè)sleep(),而后面的仍然按照正常的執(zhí)行邏輯進(jìn)行。

