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


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

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

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

