面試官:說(shuō)說(shuō)停止線程池的執(zhí)行流程?
對(duì)于我們使用的線程池 ThreadPoolExecutor 來(lái)說(shuō),停止線程池的方法有以下兩個(gè):
- shutdown():優(yōu)雅的關(guān)閉線程池,即不再接受新任務(wù),但會(huì)等待已提交任務(wù)(包括正在執(zhí)行的任務(wù)和在隊(duì)列中等待的任務(wù))執(zhí)行完畢。等待****所有任務(wù)都執(zhí)行完畢后,線程池才會(huì)進(jìn)入終止?fàn)顟B(tài)。
 - shutdownNow():嘗試停止所有正在執(zhí)行的任務(wù),并返回等待執(zhí)行的任務(wù)列表。正在執(zhí)行的任務(wù)可能會(huì)被中斷,適用于需要立即停止線程池,但不關(guān)心正在執(zhí)行的任務(wù)是否立即完成的情況下。
 
1.代碼演示
下面通過(guò)代碼案例,咱們來(lái)了解一下 shutdown() 和 shutdownNow() 方法的具體使用。
1.1 shutdown() 方法執(zhí)行
我們將線程池核心和最大線程數(shù)都設(shè)置為 2,任務(wù)隊(duì)列可以存儲(chǔ) 10 個(gè)任務(wù),一次性添加了 5 個(gè)任務(wù),每個(gè)任務(wù)執(zhí)行 2s 以上,添加完任務(wù)之后執(zhí)行停止方法,并在 1s 之后嘗試添加另一個(gè)新任務(wù),如下代碼所示:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorShutdownTest {
    public static void main(String[] args) {
        // 創(chuàng)建線程
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2,
                2,
                1000,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<Runnable>(10),
                new RejectedExecutionHandler() {
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        System.out.println("執(zhí)行拒絕策略");
                    }
                });
        // 添加任務(wù)
        for (int i = 0; i < 5; i++) {
            executor.submit(() -> {
                String tName = Thread.currentThread().getName();
                System.out.println(tName + ":開(kāi)始執(zhí)行任務(wù)!");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(tName + ":結(jié)束執(zhí)行任務(wù)!");
            });
        }
        // 停止線程
        executor.shutdown();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 添加新任務(wù)
        executor.submit(() -> System.out.println("最后一個(gè)新任務(wù)"));
    }
}以上程序的執(zhí)行結(jié)果如下:
圖片
從以上結(jié)果可以看出,執(zhí)行 shutdown() 方法后,程序會(huì)等待線程池中的所有任務(wù)全部執(zhí)行完在關(guān)閉,再次期間線程池會(huì)拒絕加入新任務(wù),并調(diào)用線程池的拒絕策略。
1.2 shutdownNow()方法執(zhí)行
如果將 shutdown() 方法換成 shutdownNow() 方法后,以上程序的執(zhí)行結(jié)果如下:
圖片
也就是說(shuō),調(diào)用 shutdownNow() 之后,正在執(zhí)行的任務(wù)會(huì)被立即停止,且任務(wù)隊(duì)列中未執(zhí)行的任務(wù)也會(huì)被清除,調(diào)用 shutdownNow() 方法后新加入的任務(wù)會(huì)被拒絕,并執(zhí)行線程池的拒絕策略。
2.shutdown()執(zhí)行流程
shutdown() 方法執(zhí)行源碼如下:
public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(SHUTDOWN);
        interruptIdleWorkers();
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
}該源碼執(zhí)行流程如下:
- 加鎖:在多線程環(huán)境下,關(guān)閉操作涉及到修改關(guān)鍵狀態(tài)和執(zhí)行一些可能影響多個(gè)線程的操作。使用鎖可以確保這些操作的原子性和一致性,避免多個(gè)線程同時(shí)進(jìn)行關(guān)閉操作導(dǎo)致數(shù)據(jù)不一致或出現(xiàn)意外情況
 - 檢查關(guān)閉權(quán)限:在關(guān)閉之前進(jìn)行狀態(tài)檢查可以確保關(guān)閉操作是合法的,避免在不適當(dāng)?shù)臅r(shí)候進(jìn)行關(guān)閉。推進(jìn)狀態(tài)可以讓其他代碼部分能夠根據(jù)當(dāng)前執(zhí)行器的狀態(tài)做出正確的反應(yīng)。
 - 將狀態(tài)設(shè)置為 SHUTDOWN:阻止新任務(wù)提交但完成現(xiàn)有任務(wù)。
 - 中斷空閑線程。
 - 調(diào)用 onShutdown 方法(鉤子方法):可能用于在關(guān)閉時(shí)執(zhí)行一些特定的清理或自定義操作,比如釋放資源等。
 - 釋放鎖。
 - 嘗試終止線程池:如果所有任務(wù)已完成的情況下,會(huì)真正的終止線程池。
 
shutdown() 方法的執(zhí)行流程如下圖所示:
圖片















 
 
 















 
 
 
 