国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

Java線程池配置的常見誤區

開發 后端
在 Java 語言中線程池是也非常重要的一部分,有 Doug Lea 大神對線程池的封裝,我們使用的時候是非常方便,但也可能會因為不了解其具體實現,對線程池的配置參數存在誤解。

[[435054]]

 前言

由于線程的創建和銷毀對操作系統來說都是比較重量級的操作,所以線程的池化在各種語言內都有實踐,當然在 Java 語言中線程池是也非常重要的一部分,有 Doug Lea 大神對線程池的封裝,我們使用的時候是非常方便,但也可能會因為不了解其具體實現,對線程池的配置參數存在誤解。

我們經常在一些技術書籍或博客上看到,向線程池提交任務時,線程池的執行邏輯如下:

  •  當一個任務被提交后,線程池首先檢查正在運行的線程數是否達到核心線程數,如果未達到則創建一個線程。
  •  如果線程池內正在運行的線程數已經達到了核心線程數,任務將會被放到 BlockingQueue 內。
  •  如果 BlockingQueue 已滿,線程池將會嘗試將線程數擴充到最大線程池容量。
  •  如果當前線程池內線程數量已經達到最大線程池容量,則會執行拒絕策略拒絕任務提交。

流程如圖(摘自美團技術博客):

流程描述沒有問題,但如果某些點未經過推敲,容易導致誤解,而且描述中的情境太理想化,如果配置時不考慮運行時環境,也會出現一些非常詭異的問題。

核心池

線程池內線程數量小于等于 coreSize 的部分我稱為核心池,核心池是線程池的常駐部分,內部的線程一般不會被銷毀,我們提交的任務也應該絕大部分都由核心池內的線程來執行。

線程創建時機的誤解

有關核心池最常見的一個誤區是沒搞清楚核心池內線程的創建時機,這個問題,我覺得甩 10% 的鍋給 Doug Lea 大神應該不算過分,因為他在文檔里寫道 “If fewer than corePoolSize threads are running, try to start a new thread with the given command as its first task”,其中 "running" 這個詞就比較有歧義,因為在我們理解里 running 是指當前線程已被操作系統調度,擁有操作系統時間分片,或者被理解為正在執行某個任務。

基于以上的理解,我們很容易就認為如果任務的 QPS 非常低,線程池內線程數量永遠也達不到 coreSize。即如果我們配置了 coreSize 為 1000,實際上 QPS 只有 1,單個任務耗時 1s,那么核心池大小就會一直是 1,即使有流量抖動,核心池也只會被擴容到 3。因為一個線程每秒執行執行一個任務,剛好不用創建新線程就足以應對 1QPS。

創建過程

但如果簡單設計一個測試,使用 jstack 打印出線程棧并數一下線程池內線程數量,會發現線程池內的線程數會隨著任務的提交而逐漸增大,直到達到 coreSize。

因為核心池的設計初衷是想它能作為常駐池,承載日常流量,所以它應該被盡快初始化,于是線程池的邏輯是在沒有達到 coreSize 之前,每一個任務都會創建一個新的線程,對應的源碼為: 

  1. public void execute(Runnable command) {  
  2.     ...  
  3.     int c = ctl.get();  
  4.     if (workerCountOf(c) < corePoolSize) { // workerCountOf() 方法是獲取線程池內線程數量  
  5.         if (addWorker(command, true))  
  6.             return;  
  7.         c = ctl.get();  
  8.     }  
  9.     ...  

而文檔里的 running 狀態也指的是線程已經被創建,我們也知道線程被創建后,會在一個 while 循環里嘗試從 BlockingQueue 里獲取并執行任務,說它正在 running 也不為過。

基于此,我們對一些高并發服務進行的預熱,其實并不是期望 JVM 能對熱點代碼做 JIT 等優化,對線程池、連接池和本地緩存的預熱才是重點。

BlockingQueue

BlockingQueue 是線程池內的另一個重要組件,首先它是線程池”生產者-消費者”模型的中間媒介,另外它也可以為大量突發的流量做緩沖,但理解和配置它也經常會出錯。

運行模型

最常見的錯誤是不理解線程池的運行模型。首先要明確的一點是線程池并沒有準確的調度功能,即它無法感知有哪些線程是處于空閑狀態的,并把提交的任務派發給空閑線程。線程池采用的是”生產者-消費者”模式,除了觸發線程創建的任務(線程的 firstTask)不會入 BlockingQueue 外,其他任務都要進入到 BlockingQueue,等待線程池內的線程消費,而任務會被哪個線程消費到完全取決于操作系統的調度。

對應的生產者源碼如下: 

  1. public void execute(Runnable command) {  
  2.     ...  
  3.     if (isRunning(c) && workQueue.offer(command)) { isRunning() 是判斷線程池處理戚狀態  
  4.         int recheck = ctl.get();  
  5.         if (! isRunning(recheck) && remove(command))  
  6.             reject(command);  
  7.         else if (workerCountOf(recheck) == 0)  
  8.             addWorker(null, false);  
  9.     }  
  10.     ...  

對應的消費者源碼如下: 

  1. private Runnable getTask() {  
  2.         for (;;) {  
  3.             ...  
  4.             Runnable r = timed ?  
  5.                 workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :  
  6.                 workQueue.take();  
  7.             if (r != null)  
  8.                 return r;  
  9.             ...  
  10.         }  
  11.     } 

BlockingQueue 的緩沖作用

基于”生產者-消費者”模型,我們可能會認為如果配置了足夠的消費者,線程池就不會有任何問題。其實不然,我們還必須考慮并發量這一因素。

設想以下情況:有 1000 個任務要同時提交到線程池內并發執行,在線程池被初始化完成的情況下,它們都要被放到 BlockingQueue 內等待被消費,在極限情況下,消費線程一個任務也沒有執行完成,那么這 1000 個請求需要同時存在于 BlockingQueue 內,如果配置的 BlockingQueue Size 小于 1000,多余的請求就會被拒絕。

那么這種極限情況發生的概率有多大呢?答案是非常大,因為操作系統對 I/O 線程的調度優先級是非常高的,一般我們的任務都是由 I/O 的準備或完成(如 tomcat 受理了 http 請求)開始的,所以很有可能被調度到的都是 tomcat 線程,它們在一直往線程池內提交請求,而消費者線程卻調度不到,導致請求堆積。

我負責的服務就發生過這種請求被異常拒絕的情況,壓測時 QPS 2000,平均響應時間為 20ms,正常情況下,40 個線程就可以平衡生產速度,不會堆積。但在 BlockingQueue Size 為 50 時,即使線程池 coreSize 為 1000,還會出現請求被線程池拒絕的情況。

這種情況下,BlockingQueue 的重要的意義就是它是一個能長時間存儲任務的容器,能以很小的代價為線程池提供緩沖。根據上文可知,線程池能支持BlockingQueue Size個任務同時提交,我們把最大同時提交的任務個數,稱為并發量,配置線程池時,了解并發量異常重要。

并發量的計算

我們常用 QPS 來衡量服務壓力,所以配置線程池參數時也經常參考這個值,但有時候 QPS 和并發量有時候相關性并沒有那么高,QPS 還要搭配任務執行時間來推算峰值并發量。

比如請求間隔嚴格相同的接口,平均 QPS 為 1000,它的并發量峰值是多少呢?我們并沒有辦法估算,因為如果任務執行時間為 1ms,那么它的并發量只有 1;而如果任務執行時間為 1s,那么并發量峰值為 1000。

可是知道了任務執行時間,就能算出并發量了嗎?也不能,因為如果請求的間隔不同,可能 1min 內的請求都在一秒內發過來,那這個并發量還要乘以 60,所以上面才說知道了 QPS 和任務執行時間,并發量也只能靠推算。

計算并發量,我一般的經驗值是 QPS*平均響應時間,再留上一倍的冗余,但如果業務重要的話,BlockingQueue Size 設置大一些也無妨(1000 或以上),畢竟每個任務占用的內存量很有限。

考慮運行時

GC

除了上面提到的各種情況下,GC 也是一個很重要的影響因素。

我們都知道 GC 是 Stop the World 的,但這里的 World 指的是 JVM,而一個請求 I/O 的準備和完成是操作系統在進行的,JVM 停止了,但操作系統還是會正常受理請求,在 JVM 恢復后執行,所以 GC 是會堆積請求的。

上文中提到的并發量計算一定要考慮到 GC 時間內堆積的請求同時被受理的情況,堆積的請求數可以通過 QPS*GC時間 來簡單得出,還有一定要記得留出冗余。

業務峰值

除此之外,配置線程池參數時,一定要考慮業務場景。

假如接口的流量大部分來自于一個定時程序,那么平均 QPS 就沒有了任何意義,線程池設計時就要考慮給 BlockingQueue 的 Size 設置一個大一些的值;而如果流量非常不平均,一天內只有某一小段時間才有高流量的話,而且線程資源緊張的情況下,就要考慮給線程池的 maxSize 留下較大的冗余;在流量尖刺明顯而響應時間不那么敏感時,也可以設置較大的 BlockingQueue,允許任務進行一定程度的堆積。

當然除了經驗和計算外,對服務做定時的壓測無疑更能幫助掌握服務真實的情況。

小結

總結線程池的配置時,我最大的感受是一定要讀源碼!讀源碼!讀源碼!只看一些書和文章的總結是無法吃透一些重要概念的,即使搞懂了大部分也很容易會在一些角落踩坑。深入理解原理后,面對復雜情況,才有靈活配置的能力。 

 

責任編輯:龐桂玉 來源: Java知音
相關推薦

2018-04-20 11:19:17

Java誤區細節

2018-04-23 10:28:01

Java誤區

2018-08-17 08:26:25

2012-05-15 02:18:31

Java線程池

2010-08-12 11:12:27

Flex誤區

2020-12-26 15:19:00

DevOps誤區開發

2020-07-10 17:40:01

人工智能網絡技術

2021-06-27 17:35:54

DevSecOps網絡安全數據泄露

2023-10-24 06:59:17

2012-05-16 09:29:25

JavaRailsJVM

2021-09-11 15:26:23

Java多線程線程池

2015-08-20 09:17:36

Java線程池

2017-11-20 14:18:32

2009-03-24 10:09:58

SaaS誤區調查

2017-11-20 08:56:54

克服容器誤區

2018-04-17 12:35:07

區塊鏈比特幣數字貨幣

2018-02-27 11:01:42

2014-07-17 09:08:20

Android L

2022-05-27 12:40:25

前端測試項目

2019-07-01 13:43:14

公共云云計算云平臺
點贊
收藏

51CTO技術棧公眾號

好男人看片在线观看免费观看国语| 国产一区二区三区欧美| 国产日韩亚洲精品| 日本一区二区三区视频在线看 | 亚洲mv大片欧洲mv大片精品| 亚洲在线免费看| 精品国产欧美| 精品国精品国产| 午夜久久中文| 欧美一区二区人人喊爽| 久久无码高潮喷水| 欧美搞黄网站| 视频在线观看99| 亚洲日本在线观看视频| 91九色在线免费视频| 亚洲ww精品| 欧美激情乱人伦| 看女生喷水的网站在线观看| 亚洲永久精品大片| 加勒比一区二区三区| 成人的网站免费观看| 免费在线观看一级毛片| 亚洲国内精品视频| 亚洲成人福利| 欧洲乱码伦视频免费| 国产成人午夜视频| 成人淫片在线看| 先锋影音资源999| 亚洲国产成人精品视频| 欧美v亚洲v| 国产精品久久久久久av福利软件 | 亚洲美女激情视频| 精品国产亚洲一区二区在线观看| 日韩美女在线看| 红桃视频欧美| 久久久久久免费看| 狠狠躁天天躁日日躁欧美| www.中文字幕久久久| 亚洲久草在线视频| 国产二级片在线| 色偷偷88欧美精品久久久| 国产日产精品久久久久久婷婷| 欧美日韩国产美| 欧美精品videosex| 日韩国产在线看| 欧美调教在线| 欧美xxxx黑人又粗又长精品| 九一九一国产精品| 中文字幕av专区| 欧美午夜片欧美片在线观看| 日本粉色视频在线观看| 在线观看亚洲一区| 91最新在线视频| 亚洲午夜av久久乱码| 日韩av三区| 精品免费一区二区三区蜜桃| 国产一区二区三区视频在线播放| a√天堂在线观看| 精品国产成人av| 欧美成人高清视频在线观看| 国产精品久久九九| 精品日韩美女的视频高清| 怡红院成人在线| 91在线直播亚洲| 91在线码无精品| 91精品久久久| 亚洲在线视频观看| 亚洲欧美综合另类在线卡通| 三级在线看中文字幕完整版| 国产精品三级网站| 国产精品国产a| 3d性欧美动漫精品xxxx软件| 国产日韩精品推荐| 91久久精品日日躁夜夜躁欧美| 国产日韩三级| 日本成年人网址| 久久精品成人欧美大片古装| eeuss鲁片一区二区三区在线观看| 欧美野外wwwxxx| 日韩免费电影一区二区| 日本道色综合久久| 激情婷婷亚洲| 成人高清免费在线| 欧美日韩一区二区视频在线观看| 91精品久久久久久蜜臀| 亚洲综合社区| 精品视频一区二区三区四区五区| 免费高清一区二区三区| 久久天天躁狠狠躁夜夜躁| 欧美国产精品v| 91九色在线播放| 中文字幕日韩一区二区三区 | 91久久偷偷做嫩草影院| 亚洲女同一区二区| 成人看片爽爽爽| 欧美男女交配视频| 日韩暖暖在线视频| 午夜影院久久久| 忘忧草精品久久久久久久高清| 嫩草嫩草嫩草嫩草| 成人www视频在线观看| 亚洲香蕉伊在人在线观| 最新亚洲精品| 伊人久久青青草| 国产精品美女xx| 亚洲高清在线观看| 久久综合给合久久狠狠狠97色69| 国产v综合v| 写真片福利在线播放| 91久久综合亚洲鲁鲁五月天| 一本色道久久综合狠狠躁的推荐| 五月开心六月丁香综合色啪| 日本私人网站在线观看| 久久99精品久久久久久久久久| 2019av中文字幕| 97视频com| 久久久久国产精品一区| 久久综合色影院| 欧美高清在线观看| 羞羞色国产精品| 日本aⅴ大伊香蕉精品视频| 最近2019年中文视频免费在线观看 | 自拍亚洲一区欧美另类| 日韩免费成人网| 欧美精品777| 欧美精品久久一区二区三区| 日韩毛片精品高清免费| 亚洲色诱最新| 亚洲精品久久| 99久久婷婷这里只有精品 | 爱高潮www亚洲精品| 久久白虎精品| www国产无套内射com| 欧日韩不卡在线视频| 亚洲免费小视频| 欧美性开放视频| 99久久久久久| 精品一区二区在线视频| 国产尤物久久久| 国产成年精品| 免费在线观看一区| 一本大道香蕉8中文在线视频 | 在线观看免费视频一区二区三区| 日本欧美视频在线观看| 先锋影音一区二区三区| 亚洲va久久久噜噜噜久久天堂| 在线观看欧美日韩| 欧美日韩久久久一区| 亚洲免费毛片网站| 91丨九色丨国产丨porny| 美女视频一区免费观看| 99在线|亚洲一区二区| 91精品国产成人观看| sdde在线播放一区二区| 亚洲大奶少妇| av永久不卡| 小说区亚洲自拍另类图片专区| 一区二区三区自拍视频| 亚洲激情五月| 亚洲精品色图| 日韩在线播放一区二区| 久久国产精品99精品国产| 精久久久久久久久久久| 国产精品1区二区.| 91最新地址在线播放| 国产嫩草影院久久久久| 亚洲电影激情视频网站| 色哟哟亚洲精品| 欧美精品久久久久久久多人混战| 欧美日韩在线视频一区| 日韩一区二区不卡| 亚洲香蕉在线观看| 麻豆app在线观看| 亚洲国产电影| 韩国精品一区二区| 欧美性猛交xxxx免费看漫画| 久久影院在线观看| 欧美日韩一区在线观看视频| 国产一级黄色电影| 成人四虎影院| 香蕉久久夜色精品国产使用方法 | 91精品国产吴梦梦| 91看片就是不一样| 午夜在线播放| 台湾色综合娱乐中文网| 日韩avvvv在线播放| 亚洲精品久久7777| 亚洲精品在线免费播放| 欧美激情18p| 国产精品一区二区欧美黑人喷潮水| 亚洲一区二区三区精品视频| 日本a级片免费| 欧美不卡高清一区二区三区| 色中色综合网| 最新国产成人在线观看| 亚洲视频在线看| 日韩精品最新在线观看| 天堂中文字幕| 国产999精品在线观看| 99精品久久|