java線程池中線程數量到底是幾
線程池配置
線程池配置,假設是:
1.最小數量是5
2.阻塞隊列容量是10
3.最大數量是20
線程池裡的業務線程數量小於最小數量(5)
第一個請求
第一個請求進來的時候,這個時候,線程池沒有線程,就創建新的工作線程(即Worker線程)。
然後,這個工作線程去處理當前請求的業務線程。
第二個請求
第二個請求進來的時候,這個時候,線程池已經有瞭一個工作線程。
但是,要註意,這個時候是不會復用線程池裡已有的工作線程的。而是創建新的工作線程。
因為,線程池裡根本沒有復用線程的概念。
說白瞭,無論線程池裡已有的這個工作線程是否在處理業務線程,即不管它空閑與否,其實都會創建新的工作線程。
第三個請求
同上,仍然創建新的工作線程。
。。。
第五個請求
同上。仍然創建新的工作線程。
註意,現在,線程池有幾個工作線程?5個。
即,每個請求進來,都創建一個新的工作線程。
小於阻塞隊列容量(10)
第六個請求
第六個請求進來的時候,提交到阻塞隊列。
然後,再慢慢消費。
具體來說,是由線程池裡的工作線程來慢慢消費。
具體消費的源碼,參考:復用線程小節。
第七個請求
同上,也是先添加到阻塞隊列。
。。。
第15個請求
同上,也是先添加到阻塞隊列。
小於最大數量(20)
第16個請求
先來看,正常情況下,阻塞隊列還沒塞滿(生產環境的容量一般是1000),就會被快速處理掉。
然後,當新的請求進來的時候,繼續丟到阻塞隊列裡面去。
這個是和上面講的一樣。
但是,我們為瞭方便理解,現在假設之前的15個請求是同時到達,即
- 前面5個請求
創建5個新的請求。
- 後面10個請求
全部丟到阻塞隊列。
這個時候,阻塞隊列已經滿瞭。接著,第16個請求進來瞭,怎麼辦?
繼續創建新的工作線程。
。。。
第35個請求
同上,繼續創建新的工作線程。
註意,這個時候,線程池裡的工作線程的數量是多少?20。
因為
- 前面5個請求,創建瞭5個新的工作線程。
- 最後面的15個請求(第16到第35),創建瞭15個新的工作線程。
所以,總共,創建瞭20個新的工作線程。線程池,總共有20個工作線程。
拒絕策略
第36個請求
假設前面的請求都沒有處理完,這個時候,來瞭第36個請求,怎麼辦?
隻能采取拒絕策略。
具體采用哪個拒絕策略?比如說,一般情況下,都是采用丟棄。
復用線程
前面說瞭,線程池裡沒有復用線程的概念。
那到底是怎麼回事呢?既然不能復用線程,那搞個線程池有個幾把用?
具體是這樣子,雖然,線程池裡的工作線程不能被復用,僅僅是指類似數據庫連接池裡的連接的那種復用,即
- 用的時候,從連接池取
- 用完瞭,歸還到連接池
線程池裡的對象復用,是基於循環,而不是用完之後再還回去。
什麼意思呢?就是工作線程,不斷的從阻塞隊列裡取業務線程,然後執行業務線程。
偽代碼
工作線程{ run(){ while(){ 1.從阻塞隊列,取業務線程 2.執行業務線程; } } }
所以,線程池和連接池的區別在於,線程池的對象是線程,可以不斷的循環讀業務線程。而連接池的對象,是用完瞭歸還到連接池裡去。
jdk源碼-java.util.concurrent.ThreadPoolExecutor#runWorker
/** * 核心步驟 * 1.從阻塞隊列,讀業務線程 * 2.執行業務線程 * * --- * Main worker run loop. Repeatedly gets tasks from queue and * executes them, while coping with a number of issues: * * 1. We may start out with an initial task, in which case we * don't need to get the first one. Otherwise, as long as pool is * running, we get tasks from getTask. If it returns null then the * worker exits due to changed pool state or configuration * parameters. Other exits result from exception throws in * external code, in which case completedAbruptly holds, which * usually leads processWorkerExit to replace this thread. * * 2. Before running any task, the lock is acquired to prevent * other pool interrupts while the task is executing, and then we * ensure that unless pool is stopping, this thread does not have * its interrupt set. * * 3. Each task run is preceded by a call to beforeExecute, which * might throw an exception, in which case we cause thread to die * (breaking loop with completedAbruptly true) without processing * the task. * * 4. Assuming beforeExecute completes normally, we run the task, * gathering any of its thrown exceptions to send to afterExecute. * We separately handle RuntimeException, Error (both of which the * specs guarantee that we trap) and arbitrary Throwables. * Because we cannot rethrow Throwables within Runnable.run, we * wrap them within Errors on the way out (to the thread's * UncaughtExceptionHandler). Any thrown exception also * conservatively causes thread to die. * * 5. After task.run completes, we call afterExecute, which may * also throw an exception, which will also cause thread to * die. According to JLS Sec 14.20, this exception is the one that * will be in effect even if task.run throws. * * The net effect of the exception mechanics is that afterExecute * and the thread's UncaughtExceptionHandler have as accurate * information as we can provide about any problems encountered by * user code. * * @param w the worker */ final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // allow interrupts boolean completedAbruptly = true; try { //從阻塞隊列裡獲取業務線程 while (task != null || (task = getTask()) != null) { w.lock(); // If pool is stopping, ensure thread is interrupted; // if not, ensure thread is not interrupted. This // requires a recheck in second case to deal with // shutdownNow race while clearing interrupt if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) wt.interrupt(); try { beforeExecute(wt, task); Throwable thrown = null; try { //執行業務線程 task.run(); } catch (RuntimeException x) { thrown = x; throw x; } catch (Error x) { thrown = x; throw x; } catch (Throwable x) { thrown = x; throw new Error(x); } finally { afterExecute(task, thrown); } } finally { task = null; w.completedTasks++; w.unlock(); } } completedAbruptly = false; } finally { processWorkerExit(w, completedAbruptly); } }
到此這篇關於java線程池中線程數量到底是幾的文章就介紹到這瞭,更多相關java 線程數量內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- java線程池中Worker線程執行流程原理解析
- Android線程池源碼閱讀記錄介紹
- 詳解Java線程池是如何重復利用空閑線程的
- Android開發中線程池源碼解析
- 深入理解Java線程池從設計思想到源碼解讀