Java 線程池之ThreadPoolExecutor學習總結

来源:https://www.cnblogs.com/shouke/archive/2022/10/31/16842890.html
-Advertisement-
Play Games

前提 java version "1.8.0_25" 池簡述 軟體開發活動中,我們經常會聽到資料庫連接池、記憶體池、線程池等各種“池”概念,這些“池”到底是什麼東西呢?程式的世界里,我們可以將池簡單的理解為一種容器類數據結構,比如列表。程式處理信息的過程中,可能會依賴某些資源或者對象(暫且統一稱之為對 ...


前提

java version "1.8.0_25"

池簡述

軟體開發活動中,我們經常會聽到資料庫連接池、記憶體池、線程池等各種“池”概念,這些“池”到底是什麼東西呢?程式的世界里,我們可以將池簡單的理解為一種容器類數據結構,比如列表。程式處理信息的過程中,可能會依賴某些資源或者對象(暫且統一稱之為對象),比如資料庫連接,來執行一些高頻操作,比如數據表查詢,此時,如果被依賴對象的存活時間比較短,那就意味著需要頻繁的創建和銷毀對象,這可能會很耗時、耗系統資源(CPU、記憶體、磁碟、網路等)。為瞭解決這個問題,進行程式設計時,可能會考慮在程式初始化時,預先創建一批所需對象,並存儲到池中,或者根據需要即時創建對象,併在使用完成後,將對象添加到池中,這樣,當程式需要(再次)使用對象時,可以直接從池中直接獲取現有的對象,節省了頻繁創建和銷毀對象帶來的資源浪費,這就是池的作用,為程式提供復用對象或者提前分配資源的能力。

ThreadPoolExecutor線程池介紹

下文僅針對線程池的一些要點做介紹

任務處理流程

核心線程池大小(corePoolSize)和最大線程池大小(maximumPoolSize)

ThreadPoolExecutor會根據corePoolSize(保持存活(不允許超時退出等)的最小工作線程數,如果設置了allowCoreThreadTimeOuttrue,則該值為0。可通過getPoolSize方法獲取該值) 和maximumPoolSize(線程池中允許的最大線程數,可通過getMaximumPoolSize獲取該值)設置的界限自動調整線程池的大小。

當通過execute(Runnable) 方法提交新任務後,如果正在運行的線程的數量小於corePoolSize,則創建新線程來處理請求,即使存在其它空閑的工作線程,否則如果正在運行的線程的數量大於corePoolSize,但小於maximumPoolSize,則僅僅在隊列已經滿時才會創建新線程來處理請求。設置corePoolSize等於maximumPoolSize則表示創建一個固定大小的線程池。

通過設置maximumPoolSize為基本無界的值,例如Integer.MAX_VALUE,則允許線程池容納任意併發任務數。大多數情況下,corePoolSizemaximumPoolSize僅在構建時設置,但也可以分別用使用setCorePoolSizesetMaximumPoolSize對其進行動態更改。

按需創建線程

預設情況下,僅在新任務到達時創建和啟動線程,即便是核心線程。可以使用prestartCoreThread或者prestartAllCoreThreads對此進行動態更改。如果使用非空隊列構造線程池,你可能會想預先啟動線程。

創建新線程

使用ThreadFactory創建新線程。如果未指定,則使用Executors.defaultThreadFactory,其創建的線程都位於相同線程組,且擁有相同的優先順序NORM_PRIORITY以及非守護狀態。通過提供不同的線程工程ThreadFactory,可以修改線程的名稱,線程組,優先順序,守護狀態等等。當newThread返回null時,ThreadFactory將無法創建線程,此時執行器繼續運行,但是可能無法執行任何任務。線程應該擁有modifyThread RuntimePermission。如果工作線程或者其它線程使用不具有該許可權的線程池,服務可能被降級:配置變更可能不會及時生效,且關閉線程池可能會保留終止但未完成的狀態。

線程保持存活時間

如果線程池當前擁有多於corePoolSize數量的線程,則空閑時間超過keepAliveTime(可通過getKeepAliveTime(TimeUnit)方法獲取)的線程將被終止,以減少資源消耗。可以通過setKeepAliveTime(long,TimeUnit)方法動態改變該參數值。使用setKeepAliveTime(Long.MAX_VALUE, NANOSECONDS)可以有效的阻止空閑線程在關閉之前終止。預設情況下,keep-alive策略僅線上程池中線程數多餘corePoolSize時起作用。keepAliveTime的值不為0的情況下,可通過allowCoreThreadTimeOut(boolean)方法將keep-alive策略應用於核心線程。

排隊(Queuing)

BlockingQueue用於傳輸和容納提交的任務。此隊列的使用與線程池大小變化相關:

  • 如果線程池中當前線程數少於corePoolSize,那麼Executor總是優先創建新線程來處理任務請求,而不是讓任務請求排隊
  • 如果線程池中當前線程數等於或者多餘corePoolSize,那麼Executor總是優先讓任務排隊,而不是創建新線程
  • 如果無法讓任務請求排隊(比如任務隊列已滿),且線程池中當前線程數未超過maximumPoolSize,則創建一個新線程來處理任務請求,否則將拒絕該任務請求

三種排隊策略:

  • 直接傳遞(Direct handoffs)

    SynchronousQueue是工作隊列(workQueue)的一個預設好選擇。它將任務交給線程,而不是保留它們。此時,如果沒有立即可用的線程,將構造新線程,因為讓任務排隊的嘗試將會失敗。此策略在處理可能具有內部依賴關係的請求集時避免鎖定。通常需要無界的maximumPoolSize,以避免拒絕新任務的提交。這反過來說明當任務平均提交速度持續大於平均處理速度時,線程數無限增長的可能性。如果使用newCachedThreadPool創建線程池則表示使用直接傳遞策略

  • 無界隊列(Unbounded queues)

    當所有核心線程都繁忙時,使用無界隊列(例如,沒有預定義容量的LinkedBlockingQueue)將導致新任務在隊列中等待,從而導致沒有多餘corePoolSize的線程被創建(maximumPoolSize的值不起任何作用)。當每個任務完全彼此獨立,互不影響執行時,這可能是合適的。例如,在網頁伺服器中, 這種排隊方式用於平滑瞬時大量請求時很有用。需要註意的是,當任務平均提交速度持續大於平均處理速度時,可能會導致無界隊列無限增長。如果使用newFixedThreadPool 創建線程池則表示使用無界隊列。

  • 有界隊列(Bounded queues)

    有界隊列(例如,ArrayBlockingQueue)配合maximumPoolSizes使用有助於防止資源耗盡,但是難以調整和控制。隊列大小和最大線程池大小需要相互權衡:使用大隊列和較小的線程池可以最大限度地減少CPU使用率,操作系統資源和上下文切換開銷,但是會導致人為的低吞吐量。如果任務頻繁被阻塞(比如I/O限制),那麼系統可以調度比我們允許的更多的線程。使用小隊列通常需要較大的線程池,這會讓CPU保持繁忙,但可能會產生不可接受的調度開銷,這也會降低吞吐量。

拒絕處理任務

Executor已關閉、使用有界的線程池、工作隊列,且達到最大值時,通過方法execute(Runnable)提交的任務將被拒絕。在任何一種情況下,execute方法調用其RejectedExecutionHandlerrejectedExecution(Runnable,ThreadPoolExecutor)方法。提供以下4種預定義處理策略:
ThreadPoolExecutor.AbortPolicy(預設策略)
拒絕任務時,處理器會拋出一個運行時異常RejectedExecutionException

ThreadPoolExecutor.CallerRunsPolicy
調用execute的線程自己運行任務。這提供了一個簡單的反饋控制機制,將會降低新任務提交的速率。

ThreadPoolExecutor.DiscardPolicy
不能被執行的任務將被拋棄

ThreadPoolExecutor.DiscardOldestPolicy
如果Executor已關閉,工作隊列隊首的任務被丟棄,然後重試執行。(重試也可能失敗,導致重覆執行前面的動作)

可以定義和使用其他類型的RejectedExecutionHandler類。這樣做需要一些謹慎,特別是當策略被設計為僅在特定容量或者隊列策略下有效時

線程運行狀態

該線程池使用了一個runState來對線程進行主要生命周期控制,具有以下值:

RUNNING: 接收新任務並且處理排隊的任務
SHUTDOWN: 不接收新任務,但是處理排隊的任務。
STOP: 不接收新任務,不處理排隊的任務,並且中斷正在進行的任務。
TIDYING: 所有任務已終止。workerCount為0。線程轉為TIDYING狀態將會運行terminated() hook方法。
TERMINATED: terminated()已經運行完。

這些值之間的數字順序很重要,為了支持有序比較,runState會隨著時間單調遞增,但不需要達到每個狀態。

狀態轉換如下:
RUNNING -> SHUTDOWN
調用shutdown()時,可能隱式的在finalize()中調用

RUNNING 或者 SHUTDOWN -> STOP
調用shutdownNow()

SHUTDOWN -> TIDYING
當工作隊列和線程池都為空時
STOP -> TIDYING
線程池為空時

TIDYING -> TERMINATED
terminated() hook方法運行完成時。

線程的析構(Finalization)

如果線程池不再被程式引用且沒有剩餘的線程,線程池將被關閉。如果希望確保未被引用的線程池被回收,即使用戶用戶忘記調用shutdown,則必須通過適當的keep-alive配置,使用更低的下限--0核心線程數或者設置allowCoreThreadTimeOut(boolean),確保未使用的線程最終會消亡。

作者:授客
微信/QQ:1033553122
全國軟體測試QQ交流群:7156436

Git地址:https://gitee.com/ishouke
友情提示:限於時間倉促,文中可能存在錯誤,歡迎指正、評論!
作者五行缺錢,如果覺得文章對您有幫助,請掃描下邊的二維碼打賞作者,金額隨意,您的支持將是我繼續創作的源動力,打賞後如有任何疑問,請聯繫我!!!
           微信打賞                        支付寶打賞                  全國軟體測試交流QQ群  
              


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、函數 數的定義有兩種:一為函數的聲明、二為函數表達式-匿名函數 函數聲明 1 function fn(){//需要函數名 2 console.log("函數聲明"); 3 } 4 fn() 函數表達式 1 const fs=function(){//需要定義變數接收 2 console.log( ...
  • Day1 根據自己的垃圾html+css水平寫出了下麵這個頁面: 首先, 首部的超鏈接都能夠鏈接上, 但是不會[更多]的彈出視窗, 右面的天氣/設置/個人也沒有做, 不會怎麼去調用 中間的圖片和搜索欄能夠正常使用, 搜索欄能夠正常搜索. 但是無法水平垂直居中, 而且, 兩個input無法靠近在一起. ...
  • 相信用過vue的小伙伴,肯定被面試官問過這樣一個問題:在vue中動態的引入圖片為什麼要使用require 有些小伙伴,可能會輕蔑一笑:呵,就這,因為動態添加src被當做靜態資源處理了,沒有進行編譯,所以要加上require, 我倒著都能背出來...... emmm... 乍一看好像說的很有道理啊... ...
  • 通過 JavaScript 在瀏覽器中獲取或設置剪貼板中的內容,常用於一鍵複製或使用網頁油猴複製限制文本 使用 ~~execCommand~~ (已棄用) 寫入文本到剪貼板 document.onclick = function() { let text = 'hello world' let do ...
  • 前言 最近無聊看直播,虎牙廣告是真多,還有一堆ghs直播間經常出現在首頁,不想看到這些直播間,於是想辦法屏蔽直播間。 源碼地址 插件地址 演示 下麵先看看未安裝插件之前 虎牙首頁一堆廣告,視頻自動播放 更過分是一堆ghs,噁心人玩意兒的出現在這裡,真是影響觀看體驗 還有左側推薦欄也很煩 安裝之後: ...
  • 1.4 超鏈接 1.4.1 基礎語法 基礎語法: <a href="網頁地址"> </a> 拓展參數: <a href="網頁地址" target="跳轉方式"> </a> | href | 跳轉鏈接地址 | | | | | target | 鏈接打開方式 | 1.4.2 錨鏈接(id參數) 每個標 ...
  • 為了提高系統吞吐率,也就是提高生產效率,核心觀點如下,系統設計也是如此 在微服務或任何其他基於事件的架構(event-driven-architecture)中,在一些用例中,一個服務可能需要我們對他們自己的本地資料庫進行修改,同時發佈一個事件。然後,該事件會被其他服務所消費。為了擁有一個一致的軟體 ...
  • 您好,我是湘王,這是我的博客園,歡迎您來,歡迎您再來~ 之前說過,AQS(抽象隊列同步器)是Java鎖機制的底層實現。既然它這麼優秀,是騾子是馬,就拉出來溜溜吧。 首先用重入鎖來實現簡單的累加,就像這樣: /** * 用重入鎖實現累加 * * @author 湘王 */ public class M ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...