每日Java并發面試系列(5):基礎篇(線程池的核心原理是什么、線程池大小設置為多少更合適、線程池哪幾種類型?ThreadLocal為什么會導致內存泄漏?)

1. 什么是線程池?它的核心原理是什么?

什么是線程池?
線程池是一種基于池化思想管理和使用線程的機制。它內部維護了多個線程,等待著分配由用戶提交的并發執行的任務。這避免了頻繁創建和銷毀線程帶來的開銷,從而提高了系統的響應速度和資源利用率。

核心原理:
線程池的核心原理是?“線程復用”?和?“資源控制”

  1. 線程復用: 傳統的“一任務一線程”模式在任務執行完畢后,線程就會銷毀。線程池則讓核心線程在執行完任務后不會立即銷毀,而是處于等待狀態,去獲取新的任務來執行。這樣就省去了頻繁創建和銷毀線程的巨大開銷(包括系統調用、內存分配、資源初始化等)。
  2. 資源控制: 線程池允許我們設置資源池的大小(核心線程數、最大線程數),從而控制并發線程的數量,防止無限制地創建線程導致系統資源被耗盡、CPU過度切換,從而保證系統的穩定性和性能。

線程池的工作流程通常通過其內部的任務隊列和一套明確的規則來管理,其核心執行邏輯可以用下圖清晰地展示:


2. 線程池大小設置為多少更加合適?

這是一個沒有固定答案的問題,需要根據具體的應用場景和硬件資源進行權衡和測試。但有一些通用的指導原則和計算公式:

核心考量因素:

  • 任務類型:任務是?CPU密集型?還是?IO密集型
    • CPU密集型:任務主要消耗CPU資源,大部分時間都在進行計算。例如:復雜的數學運算、圖像處理、視頻編碼等。
    • IO密集型:任務大部分時間在等待IO操作(如磁盤讀寫、網絡請求、數據庫查詢等),CPU空閑時間較多。

經驗公式:

  • 對于CPU密集型應用:線程數應接近CPU核心數,以避免過多的線程上下文切換開銷。
    N_threads = N_cpu + 1?(一個額外的線程用于在發生頁錯誤等暫停時,確保CPU時鐘周期不會被浪費)
  • 對于IO密集型應用:線程數可以設置得更多一些,因為CPU有很多空閑時間可以去執行其他線程的任務。
    N_threads = N_cpu * U_cpu * (1 + W/C)
    • N_cpu:CPU核心數(可通過?Runtime.getRuntime().availableProcessors()?獲取)
    • U_cpu:期望的CPU利用率(0 <= U_cpu <= 1)
    • W/C:等待時間(Wait)與計算時間(Compute)的比值

實際應用:
在實際開發中,通常先使用上述公式得到一個理論值,然后通過壓力測試來不斷調整和驗證,找到最適合當前系統的線程池大小。例如,一個常見的IO密集型應用(如Web服務器)可能會將線程池大小設置為?2 * N_cpu?到?幾倍甚至幾十倍N_cpu?之間。


3. 線程池有哪幾種類型?各有什么優缺點?

Java通過?Executors?工廠類提供了幾種常見的線程池:

線程池類型創建方法工作原理優點缺點適用場景
FixedThreadPool (固定大小線程池)Executors.newFixedThreadPool(int nThreads)核心線程數 = 最大線程數,使用無界的?LinkedBlockingQueue可以控制最大并發數,提高系統資源利用率。無界隊列,可能堆積大量請求,導致OOM。適用于處理CPU密集型任務,需要限制線程數量的場景。
CachedThreadPool (可緩存線程池)Executors.newCachedThreadPool()核心線程數為0,最大線程數為Integer.MAX_VALUE,使用同步隊列?SynchronousQueue。空閑線程存活60秒。彈性高,應對大量短期異步任務時性能好。幾乎不限制線程數,可能創建過多線程,導致OOM。適用于執行很多短期異步任務,或負載較輕的服務器。
SingleThreadExecutor (單線程池)Executors.newSingleThreadExecutor()核心線程數=最大線程數=1,使用無界的?LinkedBlockingQueue保證所有任務按提交順序串行執行。無界隊列,可能堆積大量請求,導致OOM。適用于需要順序執行任務的場景,如日志記錄。
ScheduledThreadPool (定時任務線程池)Executors.newScheduledThreadPool(int coreSize)核心線程數由參數指定,最大線程數為Integer.MAX_VALUE,使用特殊的?DelayedWorkQueue可以定時或周期性執行任務。同樣存在創建過多線程的風險。執行定時任務、周期性任務,如心跳檢測、數據同步等。

重要提示FixedThreadPool?和?SingleThreadExecutor?因為使用無界隊列,CachedThreadPool?和?ScheduledThreadPool?因為最大線程數近乎無限,在任務提交速度遠大于處理速度時,都有可能導致內存溢出(OOM)。因此,阿里巴巴Java開發手冊強制要求使用?ThreadPoolExecutor?的構造函數來手動創建線程池,以便對線程池參數有更清晰的認識和控制。


4. 什么是ThreadLocal?它的實現原理是什么?

什么是ThreadLocal?
ThreadLocal?提供了線程局部變量。這些變量與普通變量不同,每個訪問該變量的線程都有自己獨立初始化的變量副本,實現了線程間的數據隔離。

實現原理:
ThreadLocal?的核心原理在于每個?Thread?對象內部都維護了一個?ThreadLocalMap?類型的變量?threadLocals

  • ThreadLocalMap?是一個定制化的哈希表,其?Key?是?ThreadLocal?對象本身(使用弱引用),Value?是我們設置的變量副本。
  • 當我們調用?threadLocal.set(value)?時,實際上是以當前?ThreadLocal?實例為 Key,將要存儲的值作為 Value,存入當前線程的?threadLocals?這個 Map 中。
  • 當我們調用?threadLocal.get()?時,它首先獲取當前線程,然后拿到當前線程的?threadLocals?Map,再以當前?ThreadLocal?實例為 Key 去查找對應的 Value。

簡單來說,數據并不保存在?ThreadLocal?本身,而是保存在線程的?threadLocals?屬性中,由?ThreadLocal?對象作為訪問的鑰匙。


5. ThreadLocal為什么會導致內存泄漏?如何解決的?ThreadLocal的應用場景有哪些?

為什么會導致內存泄漏?
內存泄漏的根本原因是?ThreadLocalMap?中?Entry?的 Key 對?ThreadLocal?實例是弱引用(WeakReference),而 Value 是強引用

  1. 弱引用的Key: 當外界對?ThreadLocal?實例的強引用消失后(例如?threadLocal = null),由于?Entry?的 Key 是弱引用,在下次GC時,這個 Key 會被回收,導致?Entry?的?Key = null
  2. 強引用的Value: 但是,Entry?中的 Value 仍然被一個強引用關聯著(通過?Thread -> ThreadLocalMap -> Entry -> Value?這條鏈)。只要線程不死(例如線程池中的核心線程會常駐),這個 Value 對象就永遠不會被回收,從而造成內存泄漏。

如何解決?

  1. 良好編程習慣: 在使用完?ThreadLocal?后,必須手動調用其?remove()?方法,將當前線程的?ThreadLocalMap?中對應的 Entry 徹底刪除,斷開對 Value 的強引用。
  2. JDK的設計:?ThreadLocal?本身也做了一些努力,在?set(),?get(),?remove()?方法中,會嘗試清理 Key 為 null 的 Entry。但這是一種被動清理,不能完全依賴。

應用場景:

  1. 數據庫連接(Connection)和事務(Transaction)管理: 將一個連接綁定到當前線程,保證一個事務中的所有操作使用的是同一個連接。
  2. Session管理: 在Web開發中,將用戶會話信息存儲到?ThreadLocal?中,便于在同一次請求的各個層級中獲取。
  3. 全局參數傳遞: 避免在方法調用鏈中層層傳遞上下文參數(如用戶身份信息、語言環境等),直接從?ThreadLocal?中獲取。
  4. 日期格式化:?SimpleDateFormat?是非線程安全的,可以為每個線程創建一個獨立的副本。

6. CyclicBarrier和CountDownLatch有什么區別?

特性CountDownLatchCyclicBarrier
核心機制一個或多個線程等待一組操作完成一組線程相互等待,直到所有線程都到達一個公共屏障點
計數器遞減計數,不可重置遞增計數,可重置 (reset())
可重復使用否,計數器為0后不能再使用是,通過重置計數器可以循環使用
主要方法await(),?countDown()await()
等待者一個或多個等待線程所有互相等待的線程本身都是屏障點的一部分
常見應用場景主線程等待多個子線程完成任務后再繼續多線程計算數據,最后合并計算結果;多人游戲等待所有玩家準備完畢

簡單比喻:

  • CountDownLatch: 就像倒計時發車。司機(主線程)要等所有乘客(多個操作)都上車(countDown())后,才能發動汽車。
  • CyclicBarrier: 就像團隊旅行。必須所有成員(所有線程)都到達集合點(await())后,才能一起出發去下一個景點。

7. CopyOnWriteArrayList底層原理是什么?

原理:寫時復制(Copy-On-Write)

  1. 讀取: 所有讀取操作(get,?iterator)都是直接在一個不變的數組快照上進行的,不需要加鎖,性能極高且安全。
  2. 寫入/修改: 當執行寫入操作(add,?set,?remove)時,它會將底層原有的數組完整地復制(Copy)一份到一個新數組中,然后在這個新數組上進行修改操作。
  3. 更新引用: 修改完成后,將底層數組的引用指向這個新數組,替換掉舊的數組。
  4. 丟棄舊數據: 舊的數組如果沒有被引用,會被GC回收。

優缺點:

  • 優點: 讀寫分離,讀操作完全無鎖,性能非常高,非常適合讀多寫少的場景。
  • 缺點
    • 內存占用大: 每次寫操作都會復制整個數組,如果數組很大,會對內存造成壓力。
    • 數據最終一致性: 讀操作讀到的是舊數組的數據,無法實時感知到其他線程剛寫入的最新數據。不適合對數據實時性要求很高的場景。

8. ConcurrentHashMap鏈表轉紅黑樹為什么是8?

這個設計是基于概率和統計,是時間和空間上的一個權衡。

  • 目的: 為了解決哈希沖突嚴重時,鏈表過長導致的查詢性能從O(1)退化為O(n)的問題。紅黑樹是一種自平衡的二叉查找樹,查詢時間復雜度為O(log n)。
  • 為什么是8?: 根據泊松分布的概率統計,在理想的哈希函數下,一個哈希桶中節點數量達到8的概率非常低(約為一千萬分之六)。這意味著,絕大多數情況下鏈表長度都不會超過8。選擇8這個閾值,可以保證在絕大多數情況下仍然使用鏈表這種更節省空間的結構,只有在極少數極端情況下,才會轉換為紅黑樹來保證性能。
  • 樹退化為鏈表的閾值是6: 為什么不是7?這是為了避免在節點數量在8附近頻繁地轉換(比如一個節點頻繁地插入和刪除)。設置一個緩沖區間(6和8之間),可以有效防止因頻繁的增刪操作導致的不必要的樹化和退化,減少性能開銷。

9. 線程池用完以后是否需要shutdown嗎?

是的,強烈建議手動關閉。

如果不關閉,線程池中的核心線程會一直存活,阻止JVM的正常退出。

  • shutdown(): 溫和的關閉。不再接受新任務,但會等待線程池中已有任務(包括正在執行的和在隊列中等待的)執行完畢。
  • shutdownNow(): 強制的關閉。嘗試中斷所有正在執行的任務,不再處理隊列中等待的任務,返回尚未執行的任務列表。

最佳實踐: 通常在應用程序結束時(例如通過JVM的shutdown hook),調用?shutdown()?來優雅地關閉線程池。


10. Java中如何終止一個正在運行的線程?

停止一個線程的正確方式是“通知”它讓它自己停下來,而不是強制中斷它。

  1. 使用標志位(推薦): 設置一個 volatile 布爾類型的標志位,線程在運行時定期檢查這個標志。

    public class MyThread extends Thread {private volatile boolean stopped = false;public void run() {while (!stopped) {// ... 執行任務}}public void stopGracefully() {this.stopped = true;}
    }
    
  2. 使用?interrupt()?方法: 這是一個協作機制。

    • 調用線程的?interrupt()?方法并不是強制終止線程,而是設置線程的中斷狀態為?true
    • 被中斷的線程需要在自己的代碼中檢查中斷狀態并決定如何響應。
    • 如果線程處于阻塞狀態(如?sleep,?wait,?join),它會拋出?InterruptedException,并在捕獲異常后重置中斷狀態
    • 正確做法: 在任務代碼中捕獲?InterruptedException?或在循環中檢查?Thread.currentThread().isInterrupted()
    public void run() {while (!Thread.currentThread().isInterrupted()) {try {// ... 執行任務,可能會調用sleep等阻塞方法} catch (InterruptedException e) {// 捕獲異常后,通常有兩種選擇:// 1. 重新設置中斷狀態,退出循環Thread.currentThread().interrupt();break;// 2. 直接退出循環break;}}
    }
    

絕對不要使用被廢棄的?stop(),?suspend(),?resume()?方法,因為它們會強制終止線程,立即釋放它持有的所有鎖,可能導致數據不一致性和死鎖問題。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/920677.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/920677.shtml
英文地址,請注明出處:http://en.pswp.cn/news/920677.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

京東商品詳情API返回值應用實踐

一、API核心功能京東商品詳情API&#xff08;如jd.item.get或jd.union.open.goods.query&#xff09;是京東開放平臺提供的核心接口&#xff0c;用于通過商品ID&#xff08;skuId&#xff09;或店鋪ID檢索指定商品的詳細信息。該接口支持獲取商品基礎信息、價格、庫存、規格參數…

學習python第14天

匯報一下秋招進度&#xff0c;字節一面完后9天都沒給回復&#xff0c;大概率被掛了&#xff0c;但是官網還在流程中&#xff0c;我又沒有HR聯系方式&#xff0c;所以直接在平臺上反饋了&#xff0c;要么趕緊給我過&#xff0c;要么趕緊給我掛&#xff0c;耽誤時間。阿里國際一面…

監聽nacos配置中心數據的變化

RefreshScope實現nacos配置中心數據的動態刷新。如果需要監聽nacos配置中心數據的變化&#xff0c;并執行對應的業務邏輯&#xff0c;則可以使用NacosConfigListener注解。除了需要導入微服務和nacos配置中心的jar&#xff0c;還需要額外導入如下的jar&#xff1a;<dependen…

docker搭建Apisix和Apisix Dashboard

第一步&#xff1a;github下載源碼 參考&#xff1a;https://apisix.apache.org/zh/docs/apisix/installation-guide/ git clone https://github.com/apache/apisix-docker.git cd apisix-docker/example第二步&#xff1a;添加Apisix Dashboard鏡像 打開./apisix-docker/examp…

ubuntu 安裝conda, ubuntu24安裝miniConda

1. 官網下載腳本&#xff1a; Download Success | Anaconda 我選的mini版本&#xff0c;也可以選左邊的完整版 2. 下載后&#xff0c;上傳至服務器/opt下 3. 執行腳本安裝&#xff1a; sh Miniconda3-latest-Linux-x86_64.sh 4. 需要按照英文提示&#xff0c;輸入回車&#…

現代貪吃蛇游戲的進化:從經典玩法到多人在線體驗

Hi&#xff0c;我是前端人類學&#xff08;之前叫布蘭妮甜&#xff09;&#xff01; 貪吃蛇游戲自1976年誕生以來&#xff0c;已經從簡單的像素游戲發展成為具有豐富功能的現代游戲體驗。本文將通過一個功能增強版的貪吃蛇游戲&#xff0c;探討如何將經典游戲概念與現代Web技術…

加速智能經濟發展:如何助力“人工智能+”戰略在實時視頻領域的落地

2025年8月&#xff0c;國務院發布了《關于深入實施“人工智能”行動的意見》&#xff08;國發〔2025〕11號&#xff09;&#xff0c;明確提出&#xff0c;到2030年&#xff0c;我國將在人工智能技術的推動下全面邁入智能經濟與智能社會的新階段。政策強調&#xff0c;要通過推動…

從 WPF 到 Avalonia 的遷移系列實戰篇1:依賴屬性的異同點與遷移技巧

從 WPF 到 Avalonia 系列實戰篇1&#xff1a;依賴屬性的異同與實踐&#xff08;基于 BlinkingButton 控件&#xff09; 我的GitHub倉庫Avalonia學習項目包含完整的Avalonia實踐案例與代碼對比。 我的gitcode倉庫是Avalonia學習項目。 文中主要示例代碼均可在倉庫中查看&#xf…

基于開源AI大模型AI智能名片S2B2C商城小程序的產地優勢產品銷售策略研究

摘要&#xff1a;本文聚焦于在開源AI大模型AI智能名片S2B2C商城小程序的商業生態中&#xff0c;探討如何利用產地優勢進行產品銷售。通過分析不同產品類別的產地優勢&#xff0c;如阿膠類選東阿、海參類選沿海、紅酒類選海外等&#xff0c;結合開源AI大模型的技術支持、AI智能名…

大數據畢業設計選題:基于大數據的用戶貸款行為數據分析系統Spark SQL核心技術

&#x1f34a;作者&#xff1a;計算機畢設匠心工作室 &#x1f34a;簡介&#xff1a;畢業后就一直專業從事計算機軟件程序開發&#xff0c;至今也有8年工作經驗。擅長Java、Python、微信小程序、安卓、大數據、PHP、.NET|C#、Golang等。 擅長&#xff1a;按照需求定制化開發項目…

阻塞,非阻塞,同步,異步的理解

典型的IO分為兩個階段&#xff1a;數據的準備&#xff1a;根據系統IO操作的就緒狀態&#xff0c;阻塞&#xff0c;非阻塞&#xff08;從外部向內核緩沖區拷貝數據&#xff0c;應用進程的狀態 內核緩沖區上是否有數據可讀&#xff0c;數據沒有準備好&#xff0c;應用調用recv阻塞…

uniapp監聽物理返回按鈕事件

1. uniapp監聽物理返回按鈕事件uniapp 監聽頁面返回功能有使用onBackPress方法和使用onUnload方法。 1.1. 使用onBackPress方法在uniapp中&#xff0c;可以使用onBackPress方法來監聽頁面返回事件。這個方法與onLoad等生命周期方法同級&#xff0c;可以監聽左上角返回按鈕或and…

Windows server 2012安裝步驟

單機文件&#xff0c;選擇新建虛擬機如果分配太少的話會影響后續系統使用的流暢度&#xff0c;但是后續都是可以更改的這里選擇第一個即可選擇自定義安裝&#xff0c;然后點擊下一步即可然后點擊下一步&#xff0c;這里要等一段時間大小寫加數字組合,記錄一下密碼避免后面使用的…

【開題答辯全過程】以 “與我同行”中華傳統歷史數字化平臺的設計和分析-------為例,包含答辯的問題和答案

個人簡介一名14年經驗的資深畢設內行人&#xff0c;語言擅長Java、php、微信小程序、Python、Golang、安卓Android等開發項目包括大數據、深度學習、網站、小程序、安卓、算法。平常會做一些項目定制化開發、代碼講解、答辯教學、文檔編寫、也懂一些降重方面的技巧。感謝大家的…

Fortran二維數組去重(unique)算法實戰

Fortran: 去重unique算法實現對二維數組的快速去重 1 引言 2 結語 1 引言 本篇介紹去重算法unique,目的是為了保留數組中的唯一值。算法原理:首先將二維數組的每一行轉換成一個整數,然后對新構成的一維數組進行排序去重,最終得到正反索引。本程序與Matlab的Unique函數進行過…

小迪安全v2023學習筆記(七十四講)—— 驗證機制篇驗證碼繞過思路SRC挖掘演示

文章目錄前記WEB攻防——第七十四天機制驗證篇&重定向發送&響應狀態碼&跳過步驟&驗證碼回傳&枚舉驗證碼突破 - 回傳顯示&規律爆破漏洞原理案例演示回傳顯示規律爆破驗證目標 - 重定向發送&重定向用戶漏洞原理案例演示重定向發送重定向用戶驗證邏輯…

福彩雙色球第2025100期籃球號碼分析

福彩雙色球第20250100期籃球號碼分析&#xff0c;上期開出數字14&#xff0c;數字形式是2路球&#xff0c;合數偶數&#xff0c;大號區域數字&#xff01;最近十幾期籃球明顯大號區域得數字比較多&#xff0c;本期直接排除大號區域數字10-11-12-13-14-15-16。最近十幾期籃球出合…

【74LS112JK觸發器三進制】2022-10-8

緣由雙jk觸發器的工作原理是什么-其他-CSDN問答 JK觸發器和觸發器中最基本的RS觸發器結構相似&#xff0c;其區別在于&#xff0c;RS觸發器不允許R與S同時為1&#xff0c;而JK觸發器允許J與K同時為1。當J與K同時變為1的同時&#xff0c;輸出的值狀態會反轉。也就是說&#xff…

ABAP - CPI - pass header parameter and filter parameter to odata service

e.g. call the cpi service from postman, and pass the header parameter and filter parameter to it:in the CPI integration flow (iflow), create the iflow as below:deploy this iflow, then use postman to test it, set breakpoint in backend odata service:

大規模數據抓取挑戰:Python反爬蟲策略與數據去重技術全面解析

引言 在進行大規模數據抓取時&#xff0c;爬蟲面臨的兩大挑戰是&#xff1a;反爬蟲技術和數據去重。隨著網絡爬蟲的廣泛應用&#xff0c;網站和平臺越來越注重保護其數據&#xff0c;采取了各種反爬蟲措施&#xff0c;防止數據被惡意抓取。而在抓取過程中&#xff0c;如何有效去…