11111111111
知識共享平臺
知識共享平臺

討教大學平臺

  • 首頁
  • 免費課
  • 精品課
  • 討教題庫
  • 企業服務

    hot

  • 下載APP
  • 證書查詢
  • 關于我們
我問
討教號
搜索
消息
  • 我的文章

    我的關注

    我的問答

    我的秘密

    我的評論

    我的訂閱

    我的打賞

    我的錢包

    我的通知

    我的設置

    退出登錄

  • ×

    登錄

    討教 | 通行證

    登錄
    立即注冊
    忘記密碼?
    使用微信登錄

    提問 ×

    寫下你的問題,準確的表述更容易得到答案

    類型話題

    選擇支付方式
    您的討教幣 111 付費金額

    国产第一亚洲_浪货一天不做就难受呀_欧洲视频在线观看_亚洲精品一区二区三区美女

    沒那么簡單的線程池

    JAVA葵花寶典
    2019-06-26 17:19:51
    16篇 作品
    2062 總閱讀量

    原以為線程池還挺簡單的(平時常用,也分析過原理),這次是想自己動手寫一個線程池來更加深入的了解它;但在動手寫的過程中落地到細節時發現并沒想的那么容易。結合源碼對比后確實不得不佩服 DougLea 。

    我覺得大部分人直接去看 java.util.concurrent.ThreadPoolExecutor 的源碼時都是看一個大概,因為其中涉及到了許多細節處理,還有部分 AQS 的內容,所以想要理清楚具體細節并不是那么容易。

    與其挨個分析源碼不如自己實現一個簡版,當然簡版并不意味著功能缺失,需要保證核心邏輯一致。

    所以也是本篇文章的目的:

    自己動手寫一個五臟俱全的線程池,同時會了解到線程池的工作原理,以及如何在工作中合理的利用線程池。

    再開始之前建議對線程池不是很熟悉的朋友看看這幾篇:

    這里我截取了部分內容,也許可以埋個伏筆(坑)。具體請看這兩個鏈接。

    • 如何優雅的使用和理解線程池

    • 線程池中你不容錯過的一些細節

    由于篇幅限制,本次可能會分為上下兩篇。

    創建線程池

    現在進入正題,新建了一個 CustomThreadPool 類,它的工作原理如下:簡單來說就是往線程池里邊丟任務,丟的任務會緩沖到隊列里;線程池里存儲的其實就是一個個的 Thread ,他們會一直不停的從剛才緩沖的隊列里獲取任務執行。

    流程還是挺簡單。

    先來看看我們這個自創的線程池的效果如何吧:初始化了一個核心為3、最大線程數為5、隊列大小為 4 的線程池。

    先往其中丟了 10 個任務,由于阻塞隊列的大小為 4 ,最大線程數為 5 ,所以由于隊列里緩沖不了最終會創建 5 個線程(上限)。

    過段時間沒有任務提交后( sleep)則會自動縮容到三個線程(保證不會小于核心線程數)。

    構造函數

    來看看具體是如何實現的。

    下面則是這個線程池的構造函數:會有以下幾個核心參數:

    • miniSize 最小線程數,等效于 ThreadPool 中的核心線程數。

    • maxSize 最大線程數。

    • keepAliveTime 線程保活時間。

    • workQueue 阻塞隊列。

    • notify 通知接口。

    大致上都和 ThreadPool 中的參數相同,并且作用也是類似的。

    需要注意的是其中初始化了一個 workers 成員變量:

    1. /**

    2. * 存放線程池

    3. */

    4. private volatile Set<Worker> workers;


    5. public CustomThreadPool(int miniSize, int maxSize, long keepAliveTime,

    6. TimeUnit unit, BlockingQueue<Runnable> workQueue, Notify notify) {


    7. workers = new ConcurrentHashSet<>();

    8. }

    workers 是最終存放線程池中運行的線程,在 j.u.c 源碼中是一個 HashSet 所以對他所有的操作都是需要加鎖。

    我這里為了簡便起見就自己定義了一個線程安全的 Set 稱為 ConcurrentHashSet。其實原理也非常簡單,和 HashSet 類似也是借助于 HashMap 來存放數據,利用其 key 不可重復的特性來實現 set ,只是這里的 HashMap 是用并發安全的 ConcurrentHashMap 來實現的。

    這樣就能保證對它的寫入、刪除都是線程安全的。

    不過由于 ConcurrentHashMap 的 size() 函數并不準確,所以我這里單獨利用了一個 AtomicInteger 來統計容器大小。

    創建核心線程

    往線程池中丟一個任務的時候其實要做的事情還蠻多的,最重要的事情莫過于創建線程存放到線程池中了。

    當然我們不能無限制的創建線程,不然拿線程池來就沒任何意義了。于是 miniSize maxSize這兩個參數就有了它的意義。

    但這兩個參數再哪一步的時候才起到作用呢?這就是首先需要明確的。從這個流程圖可以看出第一步是需要判斷是否大于核心線程數,如果沒有則創建。結合代碼可以發現在執行任務的時候會判斷是否大于核心線程數,從而創建線程。

    worker.startTask() 執行任務部分放到后面分析。

    這里的 miniSize 由于會在多線程場景下使用,所以也用 volatile 關鍵字來保證可見性。

    隊列緩沖一旦寫入失敗則會判斷當前線程池的大小是否大于最大線程數,如果沒有則繼續創建線程執行。

    不然則執行會嘗試阻塞寫入隊列( j.u.c 會在這里執行拒絕策略)

    以上的步驟和剛才那張流程圖是一樣的,這樣大家是否有看出什么坑嘛?

    時刻小心從上面流程圖的這兩步可以看出會直接創建新的線程。

    這個過程相對于中間直接寫入阻塞隊列的開銷是非常大的,主要有以下兩個原因:

    • 創建線程會加鎖,雖說最終用的是 ConcurrentHashMap 的寫入函數,但依然存在加鎖的可能。

    • 會創建新的線程,創建線程還需要調用操作系統的 API 開銷較大。

    所以理想情況下我們應該避免這兩步,盡量讓丟入線程池中的任務進入阻塞隊列中。

    執行任務

    任務是添加進來了,那是如何執行的?

    在創建任務的時候提到過 worker.startTask() 函數:

    1. /**

    2. * 添加任務,需要加鎖

    3. * @param runnable 任務

    4. */

    5. private void addWorker(Runnable runnable) {

    6. Worker worker = new Worker(runnable, true);

    7. worker.startTask();

    8. workers.add(worker);

    9. }

    也就是在創建線程執行任務的時候會創建 Worker 對象,利用它的 startTask() 方法來執行任務。

    所以先來看看 Worker 對象是長啥樣的:其實他本身也是一個線程,將接收到需要執行的任務存放到成員變量 task 處。

    而其中最為關鍵的則是執行任務 worker.startTask() 這一步驟。

    1. public void startTask() {

    2. thread.start();

    3. }

    其實就是運行了 worker 線程自己,下面來看 run 方法。

    • 第一步是將創建線程時傳過來的任務執行( task.run),接著會一直不停的從隊列里獲取任務執行,直到獲取不到新任務了。

    • 任務執行完畢后將內置的計數器 -1 ,方便后面任務全部執行完畢進行通知。

    • worker 線程獲取不到任務后退出,需要將自己從線程池中釋放掉( workers.remove(this))。

    從隊列里獲取任務

    其實 getTask 也是非常關鍵的一個方法,它封裝了從隊列中獲取任務,同時對不需要保活的線程進行回收。很明顯,核心作用就是從隊列里獲取任務;但有兩個地方需要注意:

    • 當線程數超過核心線程數時,在獲取任務的時候需要通過保活時間從隊列里獲取任務;一旦獲取不到任務則隊列肯定是空的,這樣返回 null 之后在上文的 run() 中就會退出這個線程;從而達到了回收線程的目的,也就是我們之前演示的效果 

      關閉線程池

      最后來談談線程關閉的事;還是以剛才那段測試代碼為例,如果提交任務后我們沒有關閉線程,會發現即便是任務執行完畢后程序也不會退出。

      從剛才的源碼里其實也很容易看出來,不退出的原因是 Worker 線程一定還會一直阻塞在 task=workQueue.take(); 處,即便是線程縮容了也不會小于核心線程數。

      通過堆棧也能證明:恰好剩下三個線程阻塞于此處。

      而關閉線程通常又有以下兩種:

      • 立即關閉:執行關閉方法后不管現在線程池的運行狀況,直接一刀切全部停掉,這樣會導致任務丟失。

      • 不接受新的任務,同時等待現有任務執行完畢后退出線程池。

      立即關閉

      我們先來看第一種 立即關閉:

      1. /**

      2. * 立即關閉線程池,會造成任務丟失

      3. */

      4. public void shutDownNow() {

      5. isShutDown.set(true);

      6. tryClose(false);

      7. }


      8. /**

      9. * 關閉線程池

      10. *

      11. * @param isTry true 嘗試關閉 --> 會等待所有任務執行完畢

      12. * false 立即關閉線程池--> 任務有丟失的可能

      13. */

      14. private void tryClose(boolean isTry) {

      15. if (!isTry) {

      16. closeAllTask();

      17. } else {

      18. if (isShutDown.get() && totalTask.get() == 0) {

      19. closeAllTask();

      20. }

      21. }


      22. }


      23. /**

      24. * 關閉所有任務

      25. */

      26. private void closeAllTask() {

      27. for (Worker worker : workers) {

      28. //LOGGER.info("開始關閉");

      29. worker.close();

      30. }

      31. }


      32. public void close() {

      33. thread.interrupt();

      34. }

      很容易看出,最終就是遍歷線程池里所有的 worker 線程挨個執行他們的中斷函數。

      我們來測試一下:可以發現后面丟進去的三個任務其實是沒有被執行的。

      完事后關閉

      而正常關閉則不一樣:

      1. /**

      2. * 任務執行完畢后關閉線程池

      3. */

      4. public void shutdown() {

      5. isShutDown.set(true);

      6. tryClose(true);

      7. }來看看實際效果:回收線程


        上文或多或少提到了線程回收的事情,其實總結就是以下兩點:

        一旦執行了 shutdown/shutdownNow 方法都會將線程池的狀態置為關閉狀態,這樣只要 worker 線程嘗試從隊列里獲取任務時就會直接返回空,導致 worker 線程被回收。

        但如果我們的隊列足夠大,導致線程數都不會超過核心線程數,這樣是不會觸發回收的。

        比如這里我將隊列大小調為 10 ,這樣任務就會累計在隊列里,不會創建五個 worker 線程。

        所以一直都是 Thread-1~3 這三個線程在反復調度任務。

        總結

        本次實現了線程池里大部分核心功能,我相信只要看完并動手敲一遍一定會對線程池有不一樣的理解。

        結合目前的內容來總結下:

        • 線程池、隊列大小要設計的合理,盡量的讓任務從隊列中獲取執行。

        • 慎用 shutdownNow() 方法關閉線程池,會導致任務丟失(除非業務允許)。

        • 如果任務多,線程執行時間短可以調大 keepalive 值,使得線程盡量不被回收從而可以復用線程。

        同時下次會分享一些線程池的新特性,如:

        • 執行帶有返回值的線程。

        • 異常處理怎么辦?

        • 所有任務執行完怎么通知我?

    本網站內容僅代表作者本人的觀點,不代表本網站的觀點和看法,與本網站立場無關,如有侵權請聯系討教。
    給作者打賞,鼓勵TA抓緊創作
    0人打賞金額
    JAVA葵花寶典
    16篇 作品
    2062 總閱讀量
    評論
    您可能感興趣的文章

    項目管理服務模式

    敏捷項目管理與傳統項目管理比較

    項目管理的特點

    PMO是什么?是管項目經理的嘛?

    項目經理必須關注的開會十大關鍵問題!

    項目的組成要素

    熱門話題 更多話題
    精益生產 質量管理 智能制造
    職場效率 項目管理 討教
    AI 大數據 六西格瑪
    ×

    給作者打賞,鼓勵TA抓緊創作!

    選擇支付方式
    選擇打賞金額
    注:打賞的收益歸作者,非平臺

    微信掃描支付

    打賞金額: 1元

    ×

    給作者打賞,鼓勵TA抓緊創作!

    您的討教幣
    填寫您打賞討教幣數量
    輸入密碼

    111

    注:打賞的收益歸作者,非平臺

    微信掃描支付

    打賞金額: 1元

    国产第一亚洲_浪货一天不做就难受呀_欧洲视频在线观看_亚洲精品一区二区三区美女

          9000px;">

                99免费精品在线观看| 在线观看国产精品网站| 亚洲视频一区二区免费在线观看| 久久美女艺术照精彩视频福利播放| 91精品福利视频| 91色视频在线| 2023国产精品自拍| 国产精品久久久久久久第一福利 | 国产亚洲婷婷免费| 日韩免费电影网站| 国产精品每日更新在线播放网址| 中文字幕成人网| 最新国产成人在线观看| 亚洲综合一区二区三区| 午夜欧美在线一二页| 国精品**一区二区三区在线蜜桃| 久久av资源网| 欧美日韩一区视频| 2020国产精品自拍| 亚洲视频在线一区| 国产精品一区二区三区四区| 99久久精品国产精品久久| 91黄色免费网站| 中文字幕国产精品一区二区| 亚洲丝袜美腿综合| 日本中文字幕不卡| 欧美精品丝袜中出| 国产精品夫妻自拍| 美国毛片一区二区| 色爱区综合激月婷婷| 亚洲免费观看在线视频| 国产成人99久久亚洲综合精品| 91精品国模一区二区三区| 一区二区三区四区不卡视频| 欧美三区在线观看| 亚洲人精品午夜| 欧美亚洲国产一区在线观看网站| 综合激情网...| 一本色道久久综合亚洲91| 国产精品久久毛片| 一本大道久久a久久精品综合| 久久理论电影网| 黄色成人免费在线| 欧美高清在线视频| 97久久久精品综合88久久| 久久综合久久综合久久| 972aa.com艺术欧美| 亚洲色图视频免费播放| 91碰在线视频| 午夜视频久久久久久| 337p亚洲精品色噜噜| 久久精品国产77777蜜臀| 日本一区二区三区视频视频| 国内精品久久久久影院薰衣草| 国产午夜精品久久久久久久| 色欲综合视频天天天| 亚洲成人自拍网| 久久精品这里都是精品| 欧美一区二区三区免费| 国产成人aaa| 极品少妇一区二区三区精品视频 | 国产99久久久精品| 亚洲国产成人porn| 国产精品系列在线| 精品国产乱子伦一区| 91福利区一区二区三区| 国产不卡视频在线播放| 国内精品嫩模私拍在线| 亚洲一区二区三区爽爽爽爽爽| 日本一区二区视频在线| 日韩一级完整毛片| 欧美少妇xxx| 91麻豆国产香蕉久久精品| 九九精品视频在线看| 日本午夜一区二区| 亚洲成人tv网| 麻豆成人在线观看| 国产毛片精品视频| av午夜精品一区二区三区| 国产精品1024| 国产精品亚洲综合一区在线观看| 亚洲444eee在线观看| 久久国产精品色婷婷| 国产在线国偷精品产拍免费yy| 久久爱另类一区二区小说| 美国av一区二区| 成人午夜av在线| 91在线小视频| 91精品国产综合久久久久久| 欧美一区二区成人| 国产精品久久久久一区| 亚洲欧美一区二区三区国产精品| 日本午夜精品视频在线观看| 久久精品国产一区二区| 国产做a爰片久久毛片| 在线免费精品视频| 精品少妇一区二区三区免费观看| 国产视频一区在线观看| 亚洲最大色网站| 99麻豆久久久国产精品免费| 日本福利一区二区| 国产精品美女久久久久久2018| 午夜影院久久久| 国产乱码精品1区2区3区| 7777精品伊人久久久大香线蕉完整版 | 视频一区在线播放| 麻豆极品一区二区三区| 国产91富婆露脸刺激对白| 欧美男生操女生| 夜夜嗨av一区二区三区网页 | 亚洲gay无套男同| 欧美专区在线观看一区| 国产欧美在线观看一区| 国产高清成人在线| 91精品国产色综合久久不卡电影 | 亚洲女厕所小便bbb| 91美女片黄在线观看| 中文字幕在线播放不卡一区| 国产精品原创巨作av| 亚洲精品在线观| 色综合天天天天做夜夜夜夜做| 国产欧美日韩精品在线| 国产福利91精品一区| 亚洲美女电影在线| 久久婷婷国产综合精品青草| 国产精品久久久久aaaa樱花| 亚洲美女视频在线| 懂色av一区二区三区蜜臀| 日韩一区二区视频| 亚洲国产人成综合网站| 91国在线观看| 亚洲欧美日韩成人高清在线一区| 久久99久久久久| 日韩欧美在线一区二区三区| 亚洲国产成人91porn| 911国产精品| 美女一区二区三区在线观看| 97久久超碰国产精品| 欧美极品xxx| 成人精品鲁一区一区二区| 亚洲国产精品ⅴa在线观看| 国产91精品一区二区麻豆亚洲| 国产亚洲成aⅴ人片在线观看| 国产在线精品免费| 欧美国产欧美综合| 欧美日韩久久久一区| 蜜臀av亚洲一区中文字幕| 国产精品久久久久婷婷| 欧洲色大大久久| 久久综合综合久久综合| 一区二区三区不卡视频| 精品播放一区二区| 成人av在线一区二区三区| 成人免费视频在线观看| 91色porny| a在线播放不卡| 久久99国产精品麻豆| 亚洲精品免费视频| 亚洲视频在线一区观看| 精品sm在线观看| 欧美日韩一区 二区 三区 久久精品| 免费在线观看精品| 1000部国产精品成人观看| 日韩一区二区精品| 欧美午夜精品一区| 99riav久久精品riav| 99re这里只有精品视频首页| 蜜臂av日日欢夜夜爽一区| 亚洲日本丝袜连裤袜办公室| 综合分类小说区另类春色亚洲小说欧美 | 波波电影院一区二区三区| 亚洲成人精品一区二区| 亚洲欧美激情小说另类| 亚洲免费观看高清完整版在线| 国产精品人妖ts系列视频| 欧美电影免费观看高清完整版 | 国产精品亚洲专一区二区三区 | 在线观看中文字幕不卡| 成人免费精品视频| 国产不卡在线视频| 一本色道a无线码一区v| 欧美日韩精品一区二区| 欧美一卡二卡三卡| 日韩三级.com| 国产午夜精品一区二区| 亚洲欧洲色图综合| 久久精品国内一区二区三区| 国产超碰在线一区| 欧美又粗又大又爽| 久久久午夜电影| 亚洲欧美偷拍卡通变态| 午夜日韩在线电影| 九九九精品视频| 色94色欧美sute亚洲线路一ni| 欧美岛国在线观看| www精品美女久久久tv| 中文字幕中文字幕一区二区| 日欧美一区二区| 99re6这里只有精品视频在线观看| 国产suv精品一区二区6|