線程池的工作原理
我認爲線程池它就是一個調度任務的工具。
衆所周知在初始化線程池會給定線程池的大小,假設現在我們有 1000 個線程任務需要運行,而線程池的大小爲 10,20 個線程來調度這1000個任務。
而這裏的 10~20 個線程最後會由線程池封裝爲 ThreadPoolExecutor.Worker
對象,而這個 Worker
是實現了 Runnable 接口的,所以他自己本身就是一個線程。
深入分析
ExecutorService executorService = new ThreadPoolExecutor(2,2,0L,TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(2));
這裏我們來做一個模擬,創建了一個核心線程、最大線程數、阻塞隊列都爲2的線程池。
這裏假設線程池已經完成了預熱,也就是線程池內部已經創建好了兩個線程 Worker
。
當我們往一個線程池丟一個任務會發生什麼事呢?
- 第一步是生產者,也就是任務提供者他執行了一個 execute() 方法,本質上就是往這個內部隊列裏放了一個任務。
- 之前已經創建好了的 Worker 線程會執行一個
while
循環 ---> 不停的從這個內部隊列
裏獲取任務。(這一步是競爭的關係,都會搶着從隊列裏獲取任務,由這個隊列內部實現了線程安全。) - 獲取得到一個任務後,其實也就是拿到了一個
Runnable
對象(也就是execute(Runnable task)
這裏所提交的任務),接着執行這個Runnable
的 run() 方法,而不是 start(),這點需要注意後文分析原因。
爲什是 run() 而不是 start()
爲什麼線程池在調度的時候執行的是 Runnable
的 run()
方法,而不是 start()
方法呢?
網上大牛講的都是隻有執行了 start()
方法後操作系統纔會給我們創建一個獨立的線程來運行,而 run()
方法只是一個普通的方法調用。
而在線程池這個場景中卻恰好就是要利用它只是一個普通方法調用。
假設這裏是調用的 Runnable
的 start
方法,那會發生什麼事情。
如果我們往一個核心、最大線程數爲 2 的線程池裏丟了 1000 個任務,那麼它會額外的創建 1000 個線程,同時每個任務都是異步執行的,一下子就執行完畢了。
從而沒法做到由這兩個 Worker
線程來調度這 1000 個任務,而只有當做一個同步阻塞的 run()
方法調用時才能滿足這個要求。