進程和線程是計算機程序執行的兩個重要概念。
1.進程: 進程是操作系統分配資源的基本單位,每個進程都有自己獨立的地址空間,每啟動一個進程,系統就會為它分配內存。進程間通信比較復雜,需要用到IPC(InterProcess Communication,進程間通信)機制。
進程有五種狀態:創建、就緒、運行、阻塞和終止。
2.線程: 線程是進程中的一個執行單元,一個進程可以包含多個線程,它們共享進程的地址空間和資源。線程間通信比進程間通信要簡單很多,因為它們可以直接讀寫進程數據段(如全局變量)來進行通信。
線程也有五種狀態:創建、就緒、運行、阻塞和終止。
3.進程和線程的區別:
-
獨立性:進程間的內存空間是獨立的,而線程共享進程的內存空間。
-
資源消耗:創建或銷毀進程時,系統性能開銷明顯,而線程的資源消耗遠小于進程。
-
通信方式:進程間通信需要使用IPC機制,而線程可以直接讀寫全局變量進行通信。
-
影響:一個進程崩潰后,其他進程不受影響;而一個線程崩潰,會導致整個進程崩潰。
4.多線程和多進程的選擇: 在需要進行頻繁通信的情況下,多線程更有優勢,因為它們可以直接讀寫內存進行通信。而在需要大量計算且相互獨立的情況下,多進程可能更合適,因為它們不會因為一個進程的崩潰而影響其他進程。
并發和并行
并發:在同一時刻,有多個指令在單個CPU上交替執行。
并行:在同一時刻,有多個指令在多個CPU上同時執行。
實現多線程方式一:繼承Thread類
方法介紹
方法名 | 說明 |
---|---|
void run() | 在線程開啟后,此方法將被調用執行 |
void start() | 使此線程開始執行,Java虛擬機會調用run方法() |
實現步驟
-
定義一個類MyThread繼承Thread類
-
在MyThread類中重寫run()方法
-
創建MyThread類的對象
-
啟動線程
-
代碼演示
public class MyThread extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName()+"Hello World");}}
}
public class ThreadDemo {public static void main(String[] args) {/*多線程第一種啟動方式1.自己定義一個類繼承Thread2.重寫run方法3.創建子類的對象,并啟動線程*/MyThread t1 = new MyThread();MyThread t2 = new MyThread();t1.setName("線程一");t2.setName("線程二");//開啟線程t1.start();}
}
?
實現多線程方式二:實現Runnable接口
Thread構造方法
方法名 | 說明 |
---|---|
Thread(Runnable target) | 分配一個新的Thread對象 |
Thread(Runnable target, String name) | 分配一個新的Thread對象 |
?
實現步驟
-
定義一個類MyRunnable實現Runnable接口
-
在MyRunnable類中重寫run()方法
-
創建MyRunnable類的對象
-
創建Thread類的對象,把MyRunnable對象作為構造方法的參數
-
啟動線程
public class MyRun implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {//獲取到當前線程的對象Thread t = Thread.currentThread();System.out.println(t.getName()+"Hello World");}}
}
public class ThreadDemo {public static void main(String[] args) {/*多線程第二種啟動方式1.自己定義一個類繼承Runnable接口2.重寫run方法3.創建自己類的對象4.創建一個Thread類的對象,并開啟線程*///創建MyRun的對象//表示多線程要執行的任務MyRun mr = new MyRun();//創建線程對象Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);t1.setName("線程一");t2.setName("線程二");//開啟線程t1.start();}
}
?
實現多線程方式三: 實現Callable接口
方法介紹
方法名 | 說明 |
---|---|
V call() | 計算結果,如果無法計算結果,則拋出一個異常 |
FutureTask(Callable<V> callable) | 創建一個 FutureTask,一旦運行就執行給定的 Callable |
V get() | 如有必要,等待計算完成,然后獲取其結果 |
?
實現步驟
-
定義一個類MyCallable實現Callable接口
-
在MyCallable類中重寫call()方法
-
創建MyCallable類的對象
-
創建Future的實現類FutureTask對象,把MyCallable對象作為構造方法的參數
-
創建Thread類的對象,把FutureTask對象作為構造方法的參數
-
啟動線程
-
再調用get方法,就可以獲取線程結束之后的結果。
代碼演示
public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i <= 100; i++) {sum = sum + i;}return sum;}
}
?
public class ThreadDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {/*多線程的第三種實現方式特點:可以獲取到多線程運行到的結果1.創建一個類MyCallable實現Callable接口2.重寫call(有返回值,表示多線程運行的結果)3.創建MyCallable的對象(表示多線程要執行的任務)4.創建FutureTask的對象(作用管理多線程運行的結果)5.創建Thread類的對象,并啟動*/MyCallable mc = new MyCallable();FutureTask<Integer> ft = new FutureTask(mc);Thread thread = new Thread(ft);thread.start();//獲取多線程運行的結果Integer result = ft.get();System.out.println(result);}
}
?Java中多線程的三種主要實現方式各有其特點和適用場景。具體來說:
- 繼承Thread類:通過繼承Thread類并重寫run方法來實現多線程,這種方法簡單直觀,但在Java的單繼承體系下,可能會導致類的擴展性受限。
- 實現Runnable接口:通過實現Runnable接口并將該實現類的實例傳遞給Thread對象來創建線程,這種方式避免了單繼承的限制,提高了代碼的可讀性和可維護性。
- 實現Callable接口:與Runnable類似,但可以返回執行結果,通常與FutureTask結合使用,允許線程執行完畢后獲取返回值,增加了線程的使用場景。
總的來說,如果需要簡單的線程邏輯并且不需要額外的返回結果,可以選擇繼承Thread類或實現Runnable接口。而當需要更復雜的線程管理和控制,或者希望利用線程池等高級特性時,應該考慮使用Executor框架。
線程休眠
相關方法
方法名 | 說明 |
---|---|
static void sleep(long millis) | 使當前正在執行的線程停留(暫停執行)指定的毫秒數 |
?代碼演示
public class MyRunnable implements Runnable {@Overridepublic void run() {for (int i = 0; i < 100; i++) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "---" + i);}}
}
public class Demo {public static void main(String[] args) throws InterruptedException {/*System.out.println("睡覺前");Thread.sleep(3000);System.out.println("睡醒了");*/MyRunnable mr = new MyRunnable();Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);t1.start();t2.start();}
}
線程優先級
線程調度
在Java中,除了上述兩種基本的調度方式,還可以通過編程手段對線程的執行順序進行一定程度的控制。例如,可以在需要等待或者后執行的線程中加入sleep()
方法,使其在進入CPU調度初期就進入休眠狀態,從而給其他線程執行的機會。
優先級相關方法代碼演示
-
線程調度是指在多線程環境中,操作系統或運行時環境如何分配CPU資源給各個線程的過程。
線程調度主要有兩種調度方式:
- 分時調度:這種調度方式下,CPU的時間被平均分配給所有線程。每個線程輪流獲得CPU的使用權,執行一段時間后再切換到下一個線程。這種方式簡單公平,但可能不是最高效的方式,因為它不考慮線程的優先級和實際需求。
- 搶占式調度:這種調度方式會根據線程的優先級來決定哪個線程獲得CPU的使用權。優先級高的線程會得到更多的執行機會。如果多個線程的優先級相同,通常會隨機選擇一個線程來執行。Java使用的就是搶占式調度,這種方式能夠更好地滿足高優先級任務的需求。
方法名 | 說明 |
---|---|
final int getPriority() | 返回此線程的優先級 |
final void setPriority(int newPriority) | 更改此線程的優先級線程默認優先級是5;線程優先級的范圍是:1-10 |
public class MyCallable implements Callable<String> {@Overridepublic String call() throws Exception {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + "---" + i);}return "線程執行完畢了";}
}
?
public class Demo {public static void main(String[] args) {//優先級: 1 - 10 默認值:5MyCallable mc = new MyCallable();FutureTask<String> ft = new FutureTask<>(mc);Thread t1 = new Thread(ft);t1.setName("飛機");t1.setPriority(10);//System.out.println(t1.getPriority());//5t1.start();MyCallable mc2 = new MyCallable();FutureTask<String> ft2 = new FutureTask<>(mc2);Thread t2 = new Thread(ft2);t2.setName("坦克");t2.setPriority(1);//System.out.println(t2.getPriority());//5t2.start();}
守護線程
相關方法代碼演示
方法名 | 說明 |
---|---|
void setDaemon(boolean on) | 將此線程標記為守護線程,當運行的線程都是守護線程時,Java虛擬機將退出 |
public class MyThread1 extends Thread {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(getName() + "---" + i);}}
}
public class MyThread2 extends Thread {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName() + "---" + i);}}
}
public class Demo {public static void main(String[] args) {MyThread1 t1 = new MyThread1();MyThread2 t2 = new MyThread2();t1.setName("女神");t2.setName("備胎");//把第二個線程設置為守護線程//當普通線程執行完之后,那么守護線程也沒有繼續運行下去的必要了.t2.setDaemon(true);t1.start();t2.start();}
}
線程的生命周期
線程同步
賣票
案例需求
電影院賣100張票,
而它有3個窗口賣票,請設計一個程序模擬該電影院賣票
實現步驟
public class SellTicket extends Thread {static int ticket = 0;@Overridepublic void run() {while (true) {if (ticket < 100) {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}ticket++;System.out.println(getName() + "正在賣" + ticket + "票");} else {break;}}}
}
public class ThreadDemo { public static void main(String[] args) {/*某電影院目前正在上映國產大片,共有100張票,而它有3個窗口賣票,請設計一個程序模擬該電影院賣票*/SellTicket s1 = new SellTicket();SellTicket s2 = new SellTicket();SellTicket s3 = new SellTicket();s1.setName("窗口一");s2.setName("窗口二");s3.setName("窗口三");s1.start();s2.start();s3.start();}}
賣票案例的問題
-
賣票出現了問題
-
相同的票出現了多次
-
出現了多的票
-
-
問題產生原因
線程執行的隨機性導致的,可能在賣票過程中丟失cpu的執行權,導致出現問題
同步代碼塊解決數據安全問題
-
安全問題出現的條件
-
是多線程環境
-
有共享數據
-
有多條語句操作共享數據
-
-
如何解決多線程安全問題呢?
-
基本思想:讓程序沒有安全問題的環境
-
-
怎么實現呢?
-
把多條語句操作共享數據的代碼給鎖起來,讓任意時刻只能有一個線程執行即可
-
Java提供了同步代碼塊的方式來解決
-
同步代碼塊格式:
synchronized(任意對象) { 多條語句操作共享數據的代碼
}
synchronized(任意對象):就相當于給代碼加鎖了,任意對象就可以看成是一把鎖
同步的好處和弊端
-
好處:解決了多線程的數據安全問題
-
弊端:當線程很多時,因為每個線程都會去判斷同步上的鎖,這是很耗費資源的,無形中會降低程序的運行效率
代碼演示
public class SellTicket extends Thread {static int ticket = 0;//鎖對象,一定是唯一的@Overridepublic void run() {synchronized (SellTicket.class){while (true) {if (ticket < 100) {try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}ticket++;System.out.println(getName() + "正在賣" + ticket + "票");} else {break;}}}}
}
同步方法解決數據安全問題
同步方法的格式
同步方法:就是把synchronized關鍵字加到方法上
修飾符 static synchronized 返回值類型 方法名(方法參數) { 方法體;
}
同步靜態方法的鎖對象是什么呢?
類名.class
非靜態:this
同步靜態方法的鎖對象是當前類的Class對象。
在Java中,當使用synchronized
關鍵字修飾一個靜態方法時,這意味著這個方法被同步化,并且所有線程在訪問這個方法時必須先獲得鎖。對于靜態同步方法,這個鎖就是當前類的Class對象。這與其他非靜態同步方法使用的鎖不同,后者使用的是實例對象作為鎖。
具體來說,當一個線程嘗試訪問一個類的靜態同步方法時,它會嘗試獲取該類對應的Class對象的鎖。如果該鎖已被其他線程持有,則當前線程必須等待直到鎖被釋放。這種機制確保了同一時刻只有一個線程能夠執行同一個類的任何靜態同步方法。
此外,靜態同步方法與非靜態同步方法之間不會發生競態條件,因為它們使用的是不同的鎖對象。靜態同步方法使用的是類對象本身的鎖,而非靜態同步方法使用的是實例對象的鎖。
了解同步靜態方法的鎖對象對于編寫多線程程序和理解Java內存模型中的線程安全和并發控制非常重要。
Lock鎖
Lock鎖是Java并發編程中的一種同步機制,它提供了比synchronized關鍵字更加靈活的鎖操作。
首先,Lock鎖的優勢在于它能夠提供更廣泛的鎖操作。與synchronized相比,Lock鎖可以實現更細粒度的鎖控制,例如可重入鎖、公平鎖等。具體來說:
- 可重入鎖:允許同一個線程多次獲得鎖,而不會導致自己被阻塞。
- 公平鎖:保證等待時間最長的線程能夠先獲得鎖,避免線程饑餓現象。
- 讀寫鎖:允許多個讀線程同時訪問,但在寫線程訪問時會獨占鎖。
此外,Lock鎖的使用通常需要手動釋放,這要求開發者在finally塊中釋放鎖以確保資源的正確釋放。而synchronized則不需要手動釋放。
總的來說,Lock鎖提供了更多的功能和靈活性,但也帶來了更高的復雜性。在使用Lock鎖時,需要注意正確管理鎖的獲取和釋放,以避免死鎖或資源泄露。
Lock是接口不能直接實例化,這里采用它的實現類ReentrantLock來實例化
ReentrantLock構造方法
方法名 | 說明 |
---|---|
ReentrantLock() | 創建一個ReentrantLock的實例 |
加鎖解鎖方法
方法名 | 說明 |
---|---|
void lock() | 獲得鎖 |
void unlock() | 釋放鎖 |
代碼演示
public class Ticket implements Runnable {//票的數量private int ticket = 100;private Object obj = new Object();private ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {while (true) {//synchronized (obj){//多個線程必須使用同一把鎖.try {lock.lock();if (ticket <= 0) {//賣完了break;} else {Thread.sleep(100);ticket--;System.out.println(Thread.currentThread().getName() + "在賣票,還剩下" + ticket + "張票");}} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}// }}}}
死鎖
死鎖是指兩個或多個執行單元(例如進程、線程或事務)在等待彼此持有的資源時,導致它們都無法 progress。產生死鎖的情況通常涉及以下四個方面:
- 互斥條件:指資源至少由一個執行單元持有,且在釋放之前其他執行單元無法使用。
- 占有和等待條件:指執行單元已經持有至少一個資源,但又提出了新的資源請求,被阻塞的執行單元仍然保持著它的資源。
- 不剝奪條件:指一個執行單元獲得的資源在未使用完之前不能被強行剝奪,即使該資源有可能滿足其他等待資源的執行單元的需求。
- 循環等待條件:存在一種環形鏈,每個執行單元都在等待下一個執行單元所占有的資源。
在實際開發中,為了避免死鎖,可以采取一些策略,如盡量減少事務的大小和持續時間,避免長時間占用鎖資源,以及盡量保持事務之間訪問資源的一致性和順序,避免交叉鎖定。
下面是可以產生死鎖的一段代碼:~
public class DeadlockDemo {private static Object lock1 = new Object();private static Object lock2 = new Object();public static void main(String[] args) {Thread thread1 = new Thread(() -> {synchronized (lock1) {System.out.println("Thread 1: Holding lock 1...");try {Thread.sleep(100); // 模擬耗時操作} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread 1: Waiting for lock 2...");synchronized (lock2) {System.out.println("Thread 1: Holding lock 1 & 2...");}}});Thread thread2 = new Thread(() -> {synchronized (lock2) {System.out.println("Thread 2: Holding lock 2...");try {Thread.sleep(100); // 模擬耗時操作} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread 2: Waiting for lock 1...");synchronized (lock1) {System.out.println("Thread 2: Holding lock 1 & 2...");}}});thread1.start();thread2.start();}
}
生產者消費者
概述
生產者消費者模式是一種經典的多線程協作模型,它涉及兩類線程:生產者和消費者。
生產者:負責生產數據或對象,并將它們放入一個共享的隊列中。生產者不直接與消費者交互,而是將產品放入隊列后繼續生產,這樣可以保證生產者的效率不會因為等待消費者而降低。 消費者:負責從隊列中取出數據或對象并進行消費。消費者不需要等待生產者生產,只需檢查隊列中是否有產品可消費。這樣,消費者可以保持持續的工作狀態,提高整體的處理效率。
此外,在實現生產者消費者模式時,通常需要考慮以下幾個關鍵點:
- 共享隊列:生產者和消費者通過這個隊列進行間接通信。生產者將產品放入隊列,消費者從隊列中取出產品。
- 同步機制:需要確保當隊列滿時,生產者停止生產;當隊列空時,消費者停止消費。這通常通過使用信號量、鎖或其他同步機制來實現。
- 線程通信:在某些情況下,生產者生產了新的產品后,需要通知消費者來消費。這種通信可以通過條件變量或其他同步工具來實現。
總的來說,生產者消費者模式有效地解決了生產者和消費者之間的速度匹配問題,允許兩者以不同的速度運行,同時確保數據的安全傳輸和處理。
方法名 | 說明 |
---|---|
void wait() | 導致當前線程等待,直到另一個線程調用該對象的 notify()方法或 notifyAll()方法 |
void notify() | 喚醒正在等待對象監視器的單個線程 |
void notifyAll() | 喚醒正在等待對象監視器的所有線程 |
為方便理解采用以下示例
來源://https://www.bilibili.com/video/BV17F411T7Ao/
桌子
public class Desk {/*作用:控制生產者和消費者的執行*///是否有面條 0:沒有 1:有面條public static int foodFlag = 0;//總個數public static int count = 10;//鎖對象public static Object lock = new Object();}
生產者
public class Cook extends Thread{@Overridepublic void run() {while(true){synchronized (Desk.lock){if(Desk.count == 0){break;}else {//判斷桌子上是否有食物if(Desk.foodFlag == 1){//如果有,就等待try {Desk.lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}else {//如果沒有,就制作食物System.out.println("廚師做了一碗面條");//修改桌子上的食物狀態Desk.foodFlag = 1;//叫醒等待的消費者開吃Desk.lock.notifyAll();}}}}}
}
消費者
public class Foodie extends Thread{@Overridepublic void run() {while(true){synchronized (Desk.lock){if(Desk.count == 0){break;}else {if(Desk.foodFlag == 0){//用鎖的對象調用wait()try {//讓當前線程跟鎖進行綁定Desk.lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}else {Desk.count--;//如果有,就開吃System.out.println("吃貨正在吃面條,還能再吃"+Desk.count+"碗!!!");//吃完之后,喚醒廚師繼續做Desk.lock.notify();Desk.foodFlag = 0;}}}}}
}
二:阻塞隊列完成等待喚醒機制
生產者
public class Cook extends Thread{ArrayBlockingQueue<String> queue;public Cook(ArrayBlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while(true){//不斷的把面條放在阻塞隊列當中try {queue.put("面條");System.out.println("廚師放了一碗面條");} catch (InterruptedException e) {e.printStackTrace();}}}
}
public class Foodie extends Thread{ArrayBlockingQueue<String> queue;public Foodie(ArrayBlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while(true){//不斷從阻塞隊列中獲取面條try {String food = queue.take();System.out.println(food);} catch (InterruptedException e) {e.printStackTrace();}}}
}
public class ThreadDemo {public static void main(String[] args) {/*利用阻塞隊列完成生產者消費者(等待喚醒機制)//細節:生產者消費者必須使用同一個阻塞隊列*///1.創建阻塞隊列的對象ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);//創建線程的對象,并把阻塞隊列傳遞過去Cook c = new Cook(queue);Foodie f = new Foodie(queue);c.start();f.start();}
}
線程狀態
線程池
線程池是一種用于管理線程的資源池,它可以顯著提高多線程程序的性能和可靠性。
線程池的實現基于生產者-消費者模式,任務提交方作為生產者將任務放入線程池,而線程池中的工作線程作為消費者來執行這些任務。以下是線程池的一些關鍵特點:
- 線程復用:線程池通過重用現有線程來執行新任務,減少了頻繁創建和銷毀線程所帶來的開銷。
- 任務管理:線程池內部通常有一個工作隊列,用于存儲等待執行的任務,這樣可以實現任務的有序執行。
- 性能提升:通過減少線程創建的開銷和優化線程調度,線程池能夠提高程序的響應速度和吞吐量。
- 資源控制:線程池可以限制同時運行的線程數量,防止系統過載,同時也可以減少上下文切換的開銷。
- 編程簡化:使用線程池可以簡化多線程編程,開發者只需關注任務的實現,而不必處理線程的創建和管理細節。
- 異步執行:線程池支持異步執行任務,可以在不阻塞主線程的情況下執行耗時操作,提升用戶體驗。
- 靈活性:現代編程語言如C++提供了豐富的API和庫來支持線程池的實現,使得開發者可以根據需要定制線程池的行為。
- 并發控制:線程池可以幫助開發者更好地控制并發級別,避免資源競爭和死鎖等問題。
綜上所述,線程池是一種高效的線程管理機制,它通過池化技術優化了線程的創建、管理和調度,為并發編程提供了強大支持。在實際應用中,合理地使用線程池可以帶來顯著的性能提升和更好的資源利用率。
線程池代碼實現
-
創建線程池
-
提交任務
-
所有的任務全部執行完畢,關閉線程池
線程池-Executors默認線程池
我們可以使用Executors中所提供的靜態方法來創建線程池
static ExecutorService newCachedThreadPool() 創建一個默認的線程池 ? static newFixedThreadPool(int nThreads) 創建一個指定最多線程數量的線程池
public class MyRunnable implements Runnable{@Overridepublic void run() {for (int i = 0; i <= 100; i++) {System.out.println(Thread.currentThread().getName()+"---"+ i);}}
}
public class test8 {public static void main(String[] args) {//獲取線程池對象ExecutorService pool1 = Executors.newCachedThreadPool();//ExecutorService pool1 = Executors.newCachedThreadPool(3);//提交任務pool1.submit(new MyRunnable());//銷毀線程池/* pool1.shutdown();*/}
}
線程池-參數詳解
Java中的線程池通過ThreadPoolExecutor
類實現,它提供了靈活的參數配置來滿足不同場景的需求。以下是該類構造方法中的七個主要參數及其作用:
- corePoolSize:這是線程池的核心線程數量,即線程池中始終保持的最小線程數。即使這些線程處于空閑狀態,也不會被銷毀,除非設置了
allowCoreThreadTimeOut
屬性。 - maximumPoolSize:這是線程池允許創建的最大線程數量。當任務數量超過核心線程數時,線程池會嘗試創建新的線程來處理任務,但總數不會超過這個參數指定的值。
- keepAliveTime:當線程池中的線程數量超過核心線程數時,多余的空閑線程在被銷毀之前可以存活的時間。這個參數可以幫助控制資源使用,避免長時間持有不必要的線程資源。
- unit:與
keepAliveTime
參數配合使用,用于指定keepAliveTime
的時間單位,如TimeUnit.SECONDS
表示秒。 - workQueue:用于存放待執行任務的阻塞隊列。不同的隊列實現有不同的特性,如
ArrayBlockingQueue
、LinkedBlockingQueue
等,選擇合適的隊列對線程池的性能有重要影響。 - threadFactory:用于創建新線程的工廠。可以通過實現
ThreadFactory
接口來自定義線程的創建過程,如設置線程名稱、守護狀態等。 - handler:當線程池和隊列都滿了,無法接受新任務時的拒絕策略。常見的策略有
AbortPolicy
(默認,拋出異常)、CallerRunsPolicy
(調用者運行)和DiscardOldestPolicy
(丟棄最舊任務)等。
綜上所述,合理配置這些參數對于提高線程池的效率和穩定性至關重要。例如,根據任務的特性和系統的資源狀況來調整核心線程數和最大線程數,以及選擇適合的拒絕策略,可以在保證性能的同時避免資源的過度消耗
為方便理解看下圖 圖源://??黑馬程序員Java零基礎視頻教程_上部(Java入門,含斯坦福大學練習題+力扣算法題和大廠java面試題)_嗶哩嗶哩_bilibili
?
public class test8 {public static void main(String[] args) {ThreadPoolExecutor pool = new ThreadPoolExecutor(3,//核心線程數量,不能小于06,//最大線程數,不能小于0,最大數量 >= 核心線程數量60, //空閑線程最大存活時間TimeUnit.SECONDS,//時間單位new ArrayBlockingQueue<>(3),//任務隊列Executors.defaultThreadFactory(),Executors.defaultThreadFactory(),//創建線程工廠new ThreadPoolExecutor.AbortPolicy()//任務的拒絕策略);}
}