線程池源碼剖析——深入理解線程池

線程池源碼剖析

1. 手寫一個簡單線程池

目前業界線程池的設計,普遍採用的都是生產者——消費者模型,線程的使用方是生產者,而線程池就是一個消費者;
下面來設計一個簡易的線程池MythreadPool,它的設計原理跟我們的ThreadPoolExecutor的設計思想是一致的;

class MythreadPool {
    //用阻塞隊列來保存任務
    BlockingQueue<Runnable> workQueue;
    List<WorkThread> workers = new ArrayList<>();
    public MythreadPool(int poolSize, BlockingQueue<Runnable> blockingQueue) {
        this.workQueue = blockingQueue;
        for(int i=0; i<poolSize; i++) {
            WorkThread workThread = new WorkThread();
            workThread.start();
            workers.add(workThread);
        }
    }
    //提交任務
    public void excute(Runnable runnable) {
        workQueue.add(runnable);
    }
    class WorkThread extends Thread {
        @Override
        public void run() {
            //這裏是個死循環,所以工作的線程不會結束,會在這裏一直等待從阻塞隊列中獲取任務;
            while (true) {
                try {
                    Runnable task = workQueue.take();
                    task.run();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

public class Test2 {
    public static void main(String[] args) {
        MythreadPool mythreadPool = new MythreadPool(3, new ArrayBlockingQueue<>(3));
        mythreadPool.excute(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello my threadPool!!!!");
            }
        });
    }
}

輸出:
hello my threadPool!!!

由於沒有實現關閉線程池的方法,上述輸出是不會結束的,需強制關閉;

2. 線程池體系架構

Executor是位於juc包下的一個接口;
在這裏插入圖片描述

可以看到頂層是Executor接口,這個接口就一個方法:

  • void execute(Runnable command);
    

接下來是ExecutorService接口,這個接口作用是:

  • 提供了線程池生命週期的一些管理方法;
    在這裏插入圖片描述
  • 接下來是AbstractExecutorService,這個抽象類則是具體實現了上面ExecutorService接口中的那些方法;

最下面的就是我們常用的ThreadPoolExecutor,下面具體進行講解;

2. ThreadPoolExcutor源碼解讀

1. 工作狀態概述

ThradPoolExecutor提供了對線程生命週期的控制,規定線程池有如下五種狀態:

  • RUNNING(運行)
    • 能夠接受新的任務,也可以處理阻塞隊列裏面的任務;
  • SHUTDOWN(待關閉)
    • 不能夠接受新的任務,繼續處理阻塞隊列裏的任務;
  • STOP(停止)
    • 不能夠接受新的任務,也不會處理阻塞隊列裏的任務,並且會中斷正在處理的任務;
  • TIDYING(整理)
    • 所有的任務已經停止,ctl記錄的工作線程數爲0,線程池會變爲TIDYING狀態,此狀態線程池會執行鉤子方法terminated();
  • TERMINATED(終止)
    • 完全終止,且已經釋放了所有的資源;

根據源碼註釋,使用ctl的高三位來表示上面五種狀態,低29位來表示線程池中的工作線程數;
所謂的工作線程數,就是已經被允許start並且不允許被停止的線程;

    //ctl是一個原子數,線程安全的(CAS),初始值的設定代表RUNNING狀態和工作線程數爲0;
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
	
	//COUNT_BITS即爲32-3,爲29;
    private static final int COUNT_BITS = Integer.SIZE - 3;
	
	//表示工作線程的最大數,2^29 - 1;
	//即:00011111 11111111 11111111 11111111
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;
	
	//獲取線程池狀態
	//c 來與上CAPACITY取反,即與上:11100000 00000000 00000000 0000000,即取得c的前三位;
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
	
	//獲取工作線程數
	//c 來與上CAPACITY,即與上:00011111 11111111 11111111 11111111,即獲得c的第27位;
    private static int workerCountOf(int c)  { return c & CAPACITY; }
	
	//將狀態與線程數進行或運算,剛好就組成了ctl; 因爲rs的低27位全爲零,wc的高3位全爲0,而0與任意數
	//進行或運算的結果就是那個任意數;
    private static int ctlOf(int rs, int wc) { return rs | wc; }

2. 工作狀態的轉換

  • (1):RUNNING 轉換爲 SHUTDOWN :在調用shutdown()方法時;
  • (2):RUNNING或SHUTDOWN 轉換爲 STOP:調用shutdownNow()方法時;
  • (3):SHUTDOWN 轉化爲 TIDYING:當阻塞隊列和工作線程數都爲0時;
  • (4):STOP 轉化爲 TIDYING:當工作線程數爲0時;
  • (5):TIDYING 轉化爲 TERMINATED:當terminated()鉤子方法完成時;

在這裏插入圖片描述

1. excutor()方法

在這裏插入圖片描述

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