【多線程】線程復用的原理
在了解線程復用原理之前,我們再回顧一下之前在線程池的初始配置詳解的文章中提到的創建線程的流程

在線程提交線程任務的時候,線程池會提前判斷線程池中執行的任務是否達到核心線程數了,如果達到了核心線程數那么他就會將這個多出來的任務塞入隊列中,如果發現隊列中的任務數也達到了上限,這個時候線程池就會開始在核心線程數的基礎上創建更多的線程,當創建的線程達到了最大線程的線程數,并且線程隊列也滿了,這個時候就會觸發我們設置的拒絕策略。
看到這里大家仔細思考一下,為什么線程池會設置一個核心線程數呢?這樣做的好處有啥,其實這里就是為了滿足線程復用的作用,在線程池中,我們提交給線程池的每個任務,在線程池中稱之為worker,在線程池初始化之后,線程池就會創建我們設置的核心線程數大小的線程數,當有任務進來的時候,線程池其實并不會去新創建線程來執行剛剛進來的任務,而是初始化創建的線程中直接獲取worker中的任務,執行他的run方法,當當前這個線程任務執行完成之后,就會再去無限循環的獲取worker列表中的下一個任務,依次循環,從而達到了線程復用的目的。
知道了大概的流程,我們接下來再看看源碼。

第一段代碼
if (command == null)
throw new NullPointerException();
這段代碼對線程任務做了一個非空判斷,如果線程任務為null則會拋出空指針異常。
第二段代碼
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
workerCountOf()方法為獲取當前正在運行的線程數,如果當前運行的線程數,小于核心線程數,這個時候就會添加一個線程任務到核心線程池中。
可以看到addWorker(command, true)方法中傳入了一個true,這個布爾值代表我這次添加任務到線程池中需不需要創建額外的線程,ture就代表不需要創建額外的線程池,false就代表需要創建額外的線程。在當前這個情況,當前執行的任務的數量小于核心線程數,所以方法傳入的是ture。
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
如果代碼執行到了這里,說明當前線程任務已經大于核心線程數了,那么這個時候就需要通過isRunning(c) && workQueue.offer(command)來檢查線程池狀態是否為Running,如果為Running就通過workQueue.offer(command)方法放入到隊列中,如果通過(! isRunning(recheck) && remove(command)) 判斷線程不在Running狀態,說明線程池就已經關閉掉了,這個時候就會執行remove(command)方法來移除剛剛添加到隊列任務中的任務,并通過reject(command)執行拒絕策略。
如果發現線程在執行狀態下,這個時候就會通過workerCountOf(recheck) == 0來判斷之前的線程有沒有執行完成,如果之前的線程任務已經結束了這個時候workerCountOf(recheck)方法就會返回0,這個時候就代表這個任務可以加入線程池并通過addWorker(null, false)來創建一個新的線程
else if (!addWorker(command, false))
reject(command);
執行到了這里,這個時候就代表著線程池已經停止工作了。或者任務隊列已經滿了,這個時候線程池不能再加入更多的線程,通過addWorker來創建新的線程時,如果創建新的線程不成功,這個時候會執行reject來執行拒絕策略。
看到這里,可能大家還是會有點疑惑,這上面的代碼并沒有體現出線程復用的邏輯,其實玄機就在addWorker()這個方法,我們看下面的這張圖

在addWorker()方法中,里面的核心代碼其實是runWorker這個方法,其中入參的Worker就是我們提供給線程的任務隊列,runWorker獲取先獲取Worker中最先進來的任務,然后通過一個while方法去循環判斷,只要提供的線程任務不為null,那么她就會一直的循環執行。在這里,大家可能就會發現,我們提供給的多個線程任務,其實就是這一個while循環的線程中不斷的去執行,所以每個線程始終都在一個大的循環中,反復取任務執行任務,從而達到了線程復用的作用。
總結
其實為什么線程池需要使用的線程復用,其原因就是線程池的優點,節省了線程創建和關閉的開銷,以前我們普通執行線程,如果有1000個任務需要執行,這個時候就需要創建1000的線程,這個時候就會帶來1000的線程的創建和銷毀的開銷。如果我們使用了線程池,通過線程池線程復用的原理,我們1000的線程任務,肯能都會在5個線程了循環執行,這樣就節省了大量的線程創建銷毀的開銷,這就是使用線程復用的好處。

浙公網安備 33010602011771號