前言:
????????在Java編程中,線程池是一個強大的工具,它能夠管理和復用線程,提供高效的并發處理能力。通過線程池,我們可以有效地控制并發線程的數量,并降低線程創建和銷毀的開銷。本文將引導你深入了解Java中的線程池,探索其原理、用法和優勢,為你提供一個更高效的編程方式。
?線程池的作用就是管理線程數量,減少線程頻繁的創建和銷毀?
?線程池:
???線程池是一種用于管理和復用線程的技術,它可以有效地處理并發任務并提高程序的性能和響應能力。線程池維護著一個線程隊列,其中包含了一定數量的線程。當有新的任務到達時,線程池會從隊列中選擇一個空閑的線程來執行任務,而不是為每個任務都創建新的線程。?
線程池的工作流程:
- 創建線程池,包括初始化線程隊列和創建指定數量的線程。
- 將任務提交給線程池。可以通過將任務對象提交給線程池的方式來添加新的任務。
- 線程池從任務隊列中選擇一個空閑的線程來執行任務。
- 執行任務。線程池中的線程會執行任務對象中定義的操作。
- 任務執行完成后,線程返回線程池并等待下一個任務。
- 線程池繼續從任務隊列中選擇新的任務并分配給空閑線程,循環執行以上步驟
代碼實現線程池:
1.創建線程池的工具類? ?ExecutorService:
- public static ExecutorService newCachedThreadPool() ? ?創建一個沒有上限的線程池
- public static ExecutorService newCachedThreadPool(int int nthread) ? ?創建一個有上限的線程池
?其實第一個創建線程池不是真正沒有上限,他的上限是int的最大范圍,只不過因為實在是太大了,因此我們說沒有上限。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolExample {public static void main(String[] args) {// 創建一個固定大小為5的線程池ExecutorService threadPool = Executors.newFixedThreadPool(5);// 提交任務給線程池for (int i = 0; i < 10; i++) {final int taskId = i;threadPool.execute(new Runnable() {public void run() {try {System.out.println("開始執行任務:" + taskId);Thread.sleep(2000); // 模擬任務執行時間System.out.println("任務執行完成:" + taskId);} catch (InterruptedException e) {e.printStackTrace();}}});}// 關閉線程池threadPool.shutdown();}
}
?小技巧:
通過打斷點的方式,我們可以實時看到當前線程池中的線程數量:
?
2.自定義創建線程池對象 ?ThreadPoolExecutor:
在Java中,我們可以使用ThreadPoolExecutor來自定義創建線程池對象。ThreadPoolExecutor是ExecutorService接口的一個實現類,它允許我們靈活地配置線程池的核心線程數、最大線程數、線程存活時間等參數。
首先,我們需要導入java.util.concurrent包。然后,可以通過以下代碼創建一個自定義的線程池對象
public class test03 {public static void main(String[] args) {ThreadPoolExecutor pool = new ThreadPoolExecutor(3,//核心線程數量6,//最大線程數60,//空閑線程最大存活時間TimeUnit.SECONDS,//時間單位new ArrayBlockingQueue<>(3),//指定任務隊列最大長度Executors.defaultThreadFactory(),//創建線程工廠new ThreadPoolExecutor.AbortPolicy()//任務的拒絕策略);}
}
在上述代碼中,我們傳入了核心線程數、最大線程數、非核心線程空閑超時時間以及任務隊列等參數來創建線程池對象。你還可以根據需要,調整這些參數以滿足你的實際需求。
- 核心線程數(corePoolSize):核心線程數是線程池中一直存活的線程數量。當提交一個任務時,如果當前核心線程數還未達到設定的值,線程池會創建新的核心線程來處理任務。即使核心線程處于空閑狀態,它們也不會被回收。
- 最大線程數(maxPoolSize):最大線程數是線程池中能容納的最大線程數量。當提交的任務超過了核心線程數并且任務隊列已滿時,線程池會創建新的線程來執行任務,直到達到最大線程數。超過最大線程數的任務將會被拒絕執行。
- 非核心線程空閑超時時間(keepAliveTime):當線程池中的線程數量超過核心線程數,并且這些線程處于空閑狀態時,非核心線程會被回收。空閑超時時間設定了非核心線程的最長存活時間,超過這個時間,空閑的非核心線程將被回收。
任務拒絕策略
在Java中,任務拒絕策略用于處理線程池無法接受更多任務時的行為。當線程池已滿并且工作隊列已滿或達到最大容量時,新提交的任務可能會被拒絕執行。Java提供了四種默認的任務拒絕策略:
- AbortPolicy(中止策略):這是默認的任務拒絕策略。當線程池無法接受新任務時,新提交的任務會立即拋出RejectedExecutionException異常。
- CallerRunsPolicy(調用者運行策略):如果線程池無法接受新任務,則由提交任務的線程來執行該任務。這意味著任務的執行將回退到調用線程中執行,從而降低了整體的并發度。
- DiscardPolicy(丟棄策略):當線程池無法接受新任務時,新提交的任務將被靜默丟棄,不會拋出任何異常。這意味著被丟棄的任務將不會被執行。
- DiscardOldestPolicy(丟棄最舊策略):當線程池無法接受新任務時,線程池會丟棄工作隊列中最舊的任務(即最先提交的任務),然后嘗試再次提交新任務。
除了這四種默認的拒絕策略外,還可以通過實現RejectedExecutionHandler接口來自定義任務拒絕策略。通過實現該接口,可以定義自己的拒絕策略邏輯,例如將被拒絕的任務記錄下來或將其放入其他隊列中等。然后,可以將自定義的拒絕策略傳遞給線程
在自定義線程池中,我們需要掌握好自定義過程中的七個參數的意義。
線程池多大才算合適?
線程池大小的計算是有計算公式的,在介紹計算公式之前,我們要先講解一下什么是最大并行數
最大并行數指的是在給定的系統環境下同時執行的最大線程或任務數。
線程池大小計算公式:?
1.CPU密集型運算:
????????在CPU密集型運算中,任務主要是由CPU執行,涉及較少的I/O操作。在這種情況下,線程池的大小可以通過以下公式計算:
最大并行數+1
2.I/O密集型運算:
????????在I/O密集型運算中,任務涉及大量的I/O操作,例如讀取文件、網絡通信等。在這種情況下,由于任務執行時間中有很多時間被阻塞在I/O等待上,可以通過以下公式計算線程池的大小:
Nthreads = Ncpu * Ucpu * (1 + (W/C))
?
Nthreads 是線程池中的線程數。
Ncpu 是計算機中的CPU核心數。
Ucpu 是期望的CPU利用率(0 <= Ucpu <= 1)。它表示期望的CPU工作時間與總時間的比例。
W/C 通常設置為較大的值,以便在I/O等待期間可以讓CPU執行其他任務。
在I/O密集型任務中,通過增加線程池的大小可以更好地利用I/O等待的時間,提高整體的并發性能。然而,過量的線程數也會增加上下文切換的開銷,因此需要根據實際創建
?
?
?
?
?
?
?
?