理解線程池中線程的復用原理


線程的運行比較復雜,平常我們調用start(start0;)方法就完事了,啥時候執行run里面的代碼?經過各種狀態的轉換獲得cpu時間片,jvm就會幫我們執行run方法,執行完run方法這個線程自動消亡,遇到異常線程也會消亡,這就是一個線程的生命周期。

 

線程有兩種實現方式,一種是繼承Thread,重寫run方法,一種是自己寫一個Task實現runable接口重寫run方法,他們的啟動方式 分別是如下

//第一種方式,繼承Thread,重寫run方法后的啟動
new MyThread().start();
//第二種方式,將實現的runable接口的task作為參數傳入Thread構造方法
new Thread(new Runnable() {
    @Override
    public void run(){
        System.out.println("do something");
    }
}).start();
 

jvm執行完run方法,就會執行退出方法,該退出方法在jvm內部用C++語言寫的

void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
  //線程退出的方法
}
 

我們看這兩種線程啟動方式,感覺啟動一次只能傳一個任務進去。而且run方法又是由黑盒的jvm執行的,jvm執行完run方法就退出了,那線程池是怎么傳多個任務進去的?怎么控制run方法一直執行的?

 

這一系列疑問,要進入線程池源碼才能略知一二

 

源碼實在太復雜了,我們從前文的鋪墊開始,以正常人的思路,首先找到我們的主角---線程

1,線程池中的線程在哪?

當我們沒有將線程工廠傳入的時候使用的是默認的線程工廠

//1,ThreadPoolExecutor的構造方法眾多,但都指向一個,那就是如下
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
​
//2,將Executors.defaultThreadFactory()進入會發現默認線程工廠
public static ThreadFactory defaultThreadFactory() {
        return new DefaultThreadFactory();
}
//3,在哪觸發產生線程?在如下構造方法里,
//產生線程的s時候將這個內部類this本身傳遞進去
Worker(Runnable firstTask) {
    setState(-1); // inhibit interrupts until runWorker
    this.firstTask = firstTask;
    this.thread = getThreadFactory().newThread(this);
}
//4,進入產生線程的方法
public Thread newThread(Runnable r) {
    Thread t = new Thread(group, r,namePrefix + threadNumber.getAndIncrement(),0);
    ...//其他代碼
    return t;
}

 

 

好了我們的線程主角已經登場,但是他的登場並沒有多高大上,而是寄人籬下在一個名叫Worker的內部類里

 

這個意思是只要 new Worker(Runnable firstTask),就會產生一個線程,並且產生線程的時候將這個內部類本身this傳入進去當task。

 

這似乎跟我們前文講的差不多,new一個線程就放一個task,說好的線程復用呢?好了主角線程有了,我們的task也有了,我們再找找start方法

//線程啟動的時候
private boolean addWorker(Runnable firstTask, boolean core) {
  w = new Worker(firstTask);
  final Thread t = w.thread;
  ...//代碼
  t.start();
  ...//代碼
}
 

線程是啟動了,別忘了,線程啟動的時候我們將內部類worker對象傳入進去了,內部類Worker是實現了runable接口的,jvm執行run方法的時候就會執行Worker中的run方法

//1,這是Work中的一個方法,線程啟動的時候jvm會執行它,
public void run() {
  runWorker(this);
}
​
//2,繼續看里面的runWorker(this);很顯然this是內部類Work對象本身
final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        ../代碼
        try {
            while (task != null || (task = getTask()) != null) {
            try{
                ../代碼
                task.run();
                }catch(){
                ../代碼
                } finally {
                    ../代碼
                }
            }
            ../代碼
        } finally {
            ../代碼
        }
 }
 
        

很顯然這個runWorker很不得了,里面一個大大的while循環,當我們的task不為空的時候它就永遠在循環,並且會源源不斷的從getTask()獲取新的任務,繼續看getTask()方法

 

//很顯然這個方法是從隊列中獲取任務workQueue
private Runnable getTask() {
       ...//代碼
       // Are workers subject to culling?
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }
        
        try {
            Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}
 

很顯然,getTask()方法里面有個三元表達式,當條件為真時從任務對列workQueue.take()里面獲取要執行得任務;

 

講到這里線程復用的流程就講完了,最核心的一點是,新建一個Worker內部類就會建一個線程,並且會把這個內部類本身傳進去當作任務去執行,這個內部類的run方法里實現了一個while循環,當任務隊列沒有任務時結束這個循環,則這個線程就結束。

 

 

 https://mp.weixin.qq.com/s?__biz=MzI4NTEzMjc5Mw==&mid=2650554832&idx=1&sn=f56a25628e842edd13529e77769b2d55&chksm=f3f83346c48fba507ec005ce9bd91c6e65324b43a46b7f56a9d1542235ec9cf4b1394880b034&token=1663379705&lang=zh_CN#rd


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM