<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      如何設置線程池參數?美團給出了一個讓面試官虎軀一震的回答。

      前言:曾經自詡對線程池了如指掌,不料看了美團的一篇技術文章后才知道原來線程池的參數還可以動態調節。

      學藝不精,一邊留下了沒有技術的淚水,一邊站在美團這個巨人的肩上寫下此文,補充并記錄了自己的一點看法。

      分享給大家,希望能對你有所幫助。

      荒腔走板

      大家好,我是 why,一個四川好男人。

      今天本來應該是武漢馬拉松鳴槍起跑的日子,所以先荒腔走板說幾句馬拉松吧。

      上面的圖是我跑 2019 年成都馬拉松的時候拍的,是一對雙胞胎陪著 80 歲的父親跑全程馬拉松。

      圖片中的老人叫羅廣德,在他 75 歲之前的人生和其他的老人并無不同。

      但是經過他兒子的影響,在 75 歲的時候開始接觸跑步的。一直就沒有停下腳步,世界六大馬拉松賽(紐約、倫敦、柏林、芝加哥、東京、波士頓)他已經完成了五個。

      本來打算今年 4 月份站上波士頓馬拉松的賽道上,完成最后的挑戰。

      完成之后,他就是世界華人這個年齡段里第一個完成世界六大馬拉松賽的大滿貫跑者。

      但是由于疫情的原因,波士頓馬拉松延期舉行了。但是沒有關系,我相信老爺子的執著,我也相信他會是第一人。

      他說:“人生沒有太晚的開始,關鍵是要行動起來。現在的年輕朋友很多都缺乏鍛煉,作息時間不好,我希望年輕人都行動起來,我 80 歲都能跑步,難道你們不能跑嗎?”

      我之前說過,在賽道上你能看到很多有趣的、感動的畫面。我喜歡跑馬拉松,因為跑完之后總是能帶給我爆棚的正能量。

      人生需要一場馬拉松,你可以遲到,但是你不能缺席。

      好了,說回文章。

      經典面試題

      這次的文章還是繞回了我寫的第三篇原創文章《有的線程它死了,于是它變成一道面試題》中留下的幾個問題:

      哎,兜兜轉轉,走走停停。天道好輪回,蒼天饒過誰?

      在這篇文章中我主要回答上面拋出的這個問題:你這幾個參數的值怎么來的呀?

      要回答這個問題,我們得先說說這幾個參數是什么,請看截圖:

      其實,官方的注釋寫的都非常明白了。你看文章的時一定要結合英文,因為英文是 Doug Lea(作者)他自己寫的,表達的是作者自己的準確的想法。

      不要瞎猜好嗎?

      1.corePoolSize:the number of threads to keep in the pool, even if they are idle, unless {@code allowCoreThreadTimeOut} is set

      (核心線程數大小:不管它們創建以后是不是空閑的。線程池需要保持 corePoolSize 數量的線程,除非設置了 allowCoreThreadTimeOut。)

      2.maximumPoolSize:the maximum number of threads to allow in the pool。

      (最大線程數:線程池中最多允許創建 maximumPoolSize 個線程。)

      3.keepAliveTime:when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating。

      (存活時間:如果經過 keepAliveTime 時間后,超過核心線程數的線程還沒有接受到新的任務,那就回收。)

      4.unit:the time unit for the {@code keepAliveTime} argument

      (keepAliveTime 的時間單位。)

      5.workQueue:the queue to use for holding tasks before they are executed. This queue will hold only the {@code Runnable} tasks submitted by the {@code execute} method。

      (存放待執行任務的隊列:當提交的任務數超過核心線程數大小后,再提交的任務就存放在這里。它僅僅用來存放被 execute 方法提交的 Runnable 任務。所以這里就不要翻譯為工作隊列了,好嗎?不要自己給自己挖坑。)

      6.threadFactory:the factory to use when the executor creates a new thread。

      (線程工程:用來創建線程工廠。比如這里面可以自定義線程名稱,當進行虛擬機棧分析時,看著名字就知道這個線程是哪里來的,不會懵逼。)

      7.handler :the handler to use when execution is blocked because the thread bounds and queue capacities are reached。

      (拒絕策略:當隊列里面放滿了任務、最大線程數的線程都在工作時,這時繼續提交的任務線程池就處理不了,應該執行怎么樣的拒絕策略。)

      7 個參數介紹完了,我希望當面試官問你自定義線程池可以指定哪些參數的時候,你能回答的上來。

      當然,不能死記硬背,這樣回答起來磕磕絆絆的,像是在背書。也最好別給我回答什么:我給你舉個例子吧,就是一開始有多少多少工人....

      沒必要,真的,直接回答每個參數的名稱和含義就行了,牛逼的話你就給我說英文也行,我也能聽懂。

      這玩意大家都懂,又不抽象,你舉那例子干啥?拖延時間嗎?

      面試要求的是盡量精簡、準確的回答問題,不要讓面試官去你冗長的回答中提煉關鍵字。

      一是面試官面試體驗不好。面試完了后,常常是面試者在強調自己的面試體驗。朋友,你多慮了,你面試體驗不好,回去一頓吐槽,叫你進入下一輪面試的時候,大部分人還不是腆著個臉就來了。面試官的體驗不好,那你是真的沒有下一輪了。

      二是面試官面試都是有一定的時間限制的,有限的面試時間內,前面太啰嗦了,能問你的問題就少了。問的問題少了,面試官寫評分表的時候一想,我靠,還有好多問題沒問呢,也不知道這小子能不能回答上來,算了,就不進入下一輪了吧。

      好了好了,一不下心又暴露了幾個面試小技巧,扯遠了,說回來。

      上面的 7 個參數中,我們主要需要關心的參數是: corePoolSize、maximumPoolSize、workQueue(隊列長度)

      所以,文本主要討論這個問題:

      當我們自定義線程池的時候 corePoolSize、maximumPoolSize、workQueue(隊列長度)該如何設置?

      你以為我要給你講分 IO 密集型任務或者分 CPU 密集型任務?

      不會的,說好的是讓面試官眼前一亮、虎軀一震、直呼牛皮的答案。不騙你。

      美團騷操作

      怎么虎軀一震的呢?

      因為我看到了美團技術團隊發表的一篇文章:《Java線程池實現原理及其在美團業務中的實踐》

      第一次看到這篇文章的時候我真是眼前一亮,看到美團的這騷操作,我真是直呼牛皮。

      (哎,還是自己見的太少了。)

      這篇文章寫的很好,很全面,比如我之前說的線程執行流程,它配了一張圖,一圖勝千言:

      阻塞隊列成員表,一覽無余:

      前面都是些基礎知識,文中的后半部分才拋出了一個實際問題:

      線程池使用面臨的核心的問題在于:線程池的參數并不好配置。
      
      
      一方面線程池的運行機制不是很好理解,配置合理需要強依賴開發人員的個人經驗和知識;
      
      
      另一方面,線程池執行的情況和任務類型相關性較大,IO密集型和CPU密集型的任務運行起來的情況差異非常大。
      
      
      這導致業界并沒有一些成熟的經驗策略幫助開發人員參考。
      

      美團給出的對應的解決方案是什么呢?

      線程池參數動態化。

      盡管經過謹慎的評估,仍然不能夠保證一次計算出來合適的參數,那么我們是否可以將修改線程池參數的成本降下來,這樣至少可以發生故障的時候可以快速調整從而縮短故障恢復的時間呢?
      
      
      基于這個思考,我們是否可以將線程池的參數從代碼中遷移到分布式配置中心上,實現線程池參數可動態配置和即時生效,線程池參數動態化前后的參數修改流程對比如下:
      

      說實話看到這個圖的時候我想起之前也有這樣的想法的。

      因為有一次我這邊有個項目里面的定時任務用到了線程池,但是核心線程數和隊列長度都設置的比較大,某一次任務觸發后查出了大批數據,通過線程池提交任務,每個任務里面都會調用下游服務,導致下游服務長時間的壓力過大,也沒有做限流,所以影響了其對外提供的其他功能。

      于是我叫運維幫我在 Apollo(配置中心)調小了核心線程數,并且重啟了服務。

      那一次我就在想,我們使用的是 Apollo 天然支持動態更新,那我能不能動態的修改線程池呢?

      因為那個時候不知道一個構建好了的線程池,它的核心線程數和最大線程數是可以動態修改的。

      所以最開始的想法是監聽到參數變化后,直接弄一個新的線程池把原來的給替換掉。

      但這樣的問題是,偷天換日之后,原來的線程池里面的任務我怎么處理呢?

      我不能等原來的線程池里面的任務執行完成后再換,因為這個時候任務一定是源源不斷的過來的。

      于是就卡在了這個地方。

      說來慚愧,這塊源碼我看過幾次,但還是差點火候,學藝不精,怨不得別人。

      先勸退一波

      為了不浪費你的時間,先檢測一下你是否有閱讀本文的基礎知識儲備:

      首先,我們先自定義一個線程池:

      拿著這個線程池,當這個線程池在正常工作的前提下,我先問你兩個問題:

      1.如果這個線程池接受到了 30 個比較耗時的任務,這個時候線程池的狀態(或者說數據)是怎樣的?

      2.在前面 30 個比較耗時的任務還沒執行完成的情況下,再來多少個任務會觸發拒絕策略?

      其實這就是在問你線程池的執行流程了,簡單的說一下就是:

      1.當接收到了 30 個比較耗時的任務時,10 個核心線程數都在工作,剩下的 20 個去隊列里面排隊。這個時候和最大線程數是沒有關系的,所以和線程存活時間也就沒有關系。

      2.其實你知道這個線程池最多能接受多少任務,你就知道這個題的答案是什么了,上面的線程池中最多接受 1000(隊列長度) + 30(最大線程數) = 1030 個任務。所以當已經接收了30個任務的情況下,如果再來 1000 個比較耗時的任務,這個時候隊列也滿了,最大線程數的線程也都在工作,這個時候線程池滿載了。因此,在前面 30 個比較耗時的任務還沒執行完成的情況下,再來 1001 個任務,第 1001 個任務就會觸發線程池的拒絕策略了。

      這兩個問題你得會,如果答不上來你也別往下看了,大概率看的一臉懵逼。

      我建議你先給本文點個贊,接著去網上搜一下線程池執行流程的文章(其實美團的那篇文章也寫了執行流程),寫個 Demo 跑一下,摸清楚了,再來看這篇文章。

      巨人肩膀

      對于線程池參數到底如何設置的問題美團的那篇文章提供了一個很好的思路和解決方案,展現的是一個大而全的東西。

      但是,對于實施起來的細節就沒有具體的展示了。

      所以文本斗膽,站在巨人的肩膀上對細節處進行一些補充說明。

      1.現有的解決方案的痛點。

      2.動態更新的工作原理是什么?

      3.動態設置的注意點有哪些?

      4.如何動態指定隊列長度?

      5.這個過程中涉及到的面試題有哪些?

      下面從這五點進行展開說明。

      現有的解決方案的痛點。

      現在市面上大多數的答案都是先區分線程池中的任務是 IO 密集型還是 CPU 密集型。

      如果是 CPU 密集型的,可以把核心線程數設置為核心數+1。

      為什么要加一呢?

      《Java并發編程實戰》一書中給出的原因是:即使當計算(CPU)密集型的線程偶爾由于頁缺失故障或者其他原因而暫停時,這個“額外”的線程也能確保 CPU 的時鐘周期不會被浪費。

      看不懂是不是?沒關系我也看不懂。反正把它理解為一個備份的線程就行了。

      這個地方還有個需要注意的小點就是,如果你的服務器上部署的不止一個應用,你就得考慮其他的應用的線程池配置情況。

      經過精密的計算,你咔一下設置為核心數,結果項目部署上去了,發現還有其他的應用在和你搶 CPU,你想想難不難受。

      如果是包含 IO 操作的任務呢?這個才是我們關心的東西。

      《Java并發編程實戰》一書中給出的計算方式是這樣的:

      理想很豐滿,現實很骨感。

      我之前有個系統就是按照這個公式算出來的參數去配置的。

      結果效果并不好,甚至讓下游系統直呼受不了。

      這個東西怎么說呢,還是得記住,面試的時候有用。真實場景中只能得到一個參考值,基于這個參考值,再去進行調整。

      我們再看一下美團的那篇文章調研的現有解決方案列表:

      第一個就是我們上面說的,和實際業務場景有所偏離。

      第二個設置為 2*CPU 核心數,有點像是把任務都當做 IO 密集型去處理了。而且一個項目里面一般來說不止一個自定義線程池吧?比如有專門處理數據上送的線程池,有專門處理查詢請求的線程池,這樣去做一個簡單的線程隔離。但是如果都用這樣的參數配置的話,顯然是不合理的。

      第三個不說了,理想狀態。流量是不可能這么均衡的,就拿美團來說,下午3,4點的流量,能和 12 點左右午飯時的流量比嗎?

      基于上面的這些解決方案的痛點,美團給出了動態化配置的解決方案。

      動態更新的工作原理是什么?

      先來一個動態更新的代碼示例:

      上面的程序就是自定義了一個核心線程數為 2,最大線程數為 5,隊列長度為 10 的線程池。

      然后給它塞 15 個耗時 10 秒的任務,直接讓它 5 個最大線程都在工作,隊列長度 10 個都塞滿。

      當前的情況下,隊列里面的 10 個,前 5 個在 10 秒后會被執行,后 5 個在 20 秒后會被執行。

      再加上最大線程數正在執行的 5 個,15 個任務全部執行完全需要 3 個 10 秒即 30 秒的時間。

      這個時候,如果我們把核心線程數和最大線程數都修改為 10。

      那么 10 個任務會直接被 10 個最大線程數接管,10 秒就會被處理完成。

      剩下的 5 個任務會在 10 秒后被執行完成。

      所以,15 個任務執行完成需要 2 個 10 秒即 20 秒的時間處理完成了。

      看一下上面程序的打印日志:

      效果實現了,我先看一下原理是什么。

      先看 setCorePoolSize 方法:

      這個方法在美團的文章中也說明了:

      在運行期線程池使用方調用此方法設置corePoolSize之后,線程池會直接覆蓋原來的corePoolSize值,并且基于當前值和原始值的比較結果采取不同的處理策略。

      對于當前值小于當前工作線程數的情況,說明有多余的worker線程,此時會向當前idle的worker線程發起中斷請求以實現回收,多余的worker在下次idel的時候也會被回收;

      對于當前值大于原始值且當前隊列中有待執行任務,則線程池會創建新的worker線程來執行隊列任務,setCorePoolSize具體流程如下:

      看了美團的那篇文章后,我又去看了 Spring 的 ThreadPoolTaskExecutor類 (就是對JDK ThreadPoolExecutor 的一層包裝,可以理解為裝飾者模式)的 setCorePoolSize 方法: 注釋上寫的清清楚楚,可以在線程池運行時修改該參數。

      而且,你再品一品 JDK 的源碼,其實源碼也體現出了有修改的含義的,兩個值去做差值,只是第一次設置的時候原來的值為 0 而已。

      哎,當時沒有細細研究,恨自己看源碼的時候不仔細。

      接著看 setMaximumPoolSize 源碼:

      這個地方就很簡單了,邏輯不太復雜。

      1.首先是參數合法性校驗。

      2.然后用傳遞進來的值,覆蓋原來的值。

      3.判斷工作線程是否是大于最大線程數,如果大于,則對空閑線程發起中斷請求。

      經過前面兩個方法的分析,我們知道了最大線程數和核心線程數可以動態調整。

      動態設置的注意點有哪些?

      調整的時候可能會出現核心線程數調整之后無效的情況,比如下面這種:

      改變之前的核心線程數是 2,最大線程數為 5,我們動態修改核心線程數為 10。

      但是從日志還是可以看出,修改之后核心線程數確實變成了 10,但活躍線程數還是為 5。

      而且我調用了 prestartCoreThread 方法,該方法見名知意,你也知道是啟動所有的核心線程數,所有不存在線程沒有創建的問題。

      這是為什么呢?

      源碼之下無秘密,我帶你去看一眼:

      java.util.concurrent.ThreadPoolExecutor#getTask

      在這個方法中我們可以看到,如果工作線程數大于最大線程數,則對工作線程數量進行減一操作,然后返回 null。

      所以,這個地方的實際流程應該是: 創建新的工作線程 worker,然后工作線程數進行加一操作。 運行創建的工作線程 worker,開始獲取任務 task。 工作線程數量大于最大線程數,對工作線程數進行減一操作。 返回 null,即沒有獲取到 task。 清理該任務,流程結束。

      這樣一加一減,所以真正在執行任務的工作線程數的數量一直沒有發生變化,也就是最大線程數。

      怎么解決這個問題呢?

      答案已經呼之欲出啦。

      設置核心線程數的時候,同時設置最大線程數即可。其實可以把二者設置為相同的值:

      這樣,活動線程數就能正常提高了。

      有的小伙伴就會問了:如果調整之后把活動線程數設置的值太大了,豈不是業務低峰期我們還需要人工把值調的小一點?

      不存在的,還記得前面介紹 corePoolSize 參數的含義時的注解嗎:

      當 allowCoreThreadTimeOut 參數設置為 true 的時候,核心線程在空閑了 keepAliveTime 的時間后也會被回收的,相當于線程池自動給你動態修改了。

      如何動態指定隊列長度?

      前面介紹了最大線程數和核心線程數的動態設置,但是你發現了嗎,并沒有設置隊列長度的 set 方法啊?

      有的小機靈鬼說先獲取 Queue 對象出來再看一下呢?

      還是沒有,這可咋整呢?

      首先我們看一下為什么沒有提供隊列長度的 set 方法呢:

      因為隊列的 capacity 是被 final 修飾了呀。

      但是美團的那篇文章明明說了,他們也支持隊列的動態調整呀:

      可是沒有詳細說明,但是別著急,接著看后面的內容可以發現他們有一個名字為 ResizableCapacityLinkedBlockIngQueue 的隊列:

      很明顯,這是一個自定義隊列了。

      我們也可以按照這個思路自定義一個隊列,讓其可以對 Capacity 參數進行修改即可。

      操作起來也非常方便,把 LinkedBlockingQueue 粘貼一份出來,修改個名字,然后把 Capacity 參數的 final 修飾符去掉,并提供其對應的 get/set 方法。

      然后在程序里面把原來的隊列換掉:

      運行起來看看效果:

      可以看到,隊列大小確實從 10 變成了 100,隊列使用度從 100% 降到了 9%。

      我后來去看了美團的那篇文章下面的評論,有個評論是這樣的:

      果然不出我所料。

      這個過程中涉及到的面試題有哪些?

      問題一:線程池被創建后里面有線程嗎?如果沒有的話,你知道有什么方法對線程池進行預熱嗎?

      線程池被創建后如果沒有任務過來,里面是不會有線程的。如果需要預熱的話可以調用下面的兩個方法:

      全部啟動:

      僅啟動一個:

      問題二:核心線程數會被回收嗎?需要什么設置?

      核心線程數默認是不會被回收的,如果需要回收核心線程數,需要調用下面的方法:

      allowCoreThreadTimeOut 該值默認為 false。

      最后說一句(求關注)

      點個贊吧,周更很累的,不要白嫖我,需要一點正反饋。

      才疏學淺,難免會有紕漏,如果你發現了錯誤的地方,由于本號沒有留言功能,還請你加我微信給我指出來,我對其加以修改。(我每篇技術文章都有這句話,我是認真的說的。)

      感謝您的閱讀,我堅持原創,十分歡迎并感謝您的關注。

      我是why技術,一個不是大佬,但是喜歡分享,又暖又有料的四川好男人。

      歡迎關注公眾號【why技術】,堅持輸出原創。分享技術、品味生活,愿你我共同進步。

      posted @ 2020-04-13 12:31  why技術  閱讀(35083)  評論(24)    收藏  舉報
      主站蜘蛛池模板: 人人玩人人添人人澡超碰| 韩国av无码| 久久夜色精品国产亚av| 四虎影视一区二区精品| 国内精品久久久久影院薰衣草| 精品国产自在久久现线拍| 日韩一区二区三区在线观院| h无码精品3d动漫在线观看| 亚洲黄色一级片在线观看| 人妻中文字幕不卡精品| 老司机午夜精品视频资源| 成人亚洲一级午夜激情网| 亚洲经典在线中文字幕| 亚洲毛片多多影院| 午夜成人鲁丝片午夜精品| 日韩一卡二卡三卡四卡五卡| 精品无码成人片一区二区| 人妻少妇偷人无码视频| 国产高清在线精品一本大道| 精品国产午夜福利在线观看| 国产超高清麻豆精品传媒麻豆精品| 日韩美女视频一区二区三区| 国产精品久久久久9999高清| 国产真实乱对白精彩久久| 国产精品福利中文字幕| 国产日韩成人内射视频| 亚洲精品综合一区二区在线| 天堂V亚洲国产V第一次| 久久亚洲国产品一区二区| 国产91精品调教在线播放| 国产一区二区三区美女| 免费又黄又爽1000禁片| 国产午夜精品福利在线观看| 欧美成人片在线观看| av无码一区二区大桥久未| 小污女小欲女导航| 男女一边摸一边做爽爽| 国产成人午夜福利在线播放| 国产成人A在线视频免费| 欧美交a欧美精品喷水| 农村乱色一区二区高清视频|