Executors如何創建線程池?
Executors 類是從 JDK 1.5 開始就新增的線程池創建的靜態工廠類,它就是創建線程池的,但是很多的大廠已經不建議使用該類去創建線程池。原因在於,該類創建的很多線程池的內部使用了無界任務隊列,在並發量很大的情況下會導致 JVM 拋出 OutOfMemoryError,直接讓 JVM 崩潰,影響嚴重。
但是 Executors 類究竟是如何使用的?
1. newFixedThreadPool,創建定長線程池,每當提交一個任務就創建一個線程,直到達到線程池的最大數量,這時線程數量不再變化,當線程發生錯誤結束時,線程池會補充一個新的線程。
package constxiong.concurrency.a011;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 測試創建定長線程池
* @author ConstXiong
*/
public class TestNewFixedThreadPool {
public static void main(String[] args) {
//創建工作線程數為 3 的線程池,每當提交一個任務就創建一個線程,直到達到線程池的最大數量,這時線程數量不再變化,當線程發生錯誤結束時,線程池會補充一個新的線程
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
//提交 6 個任務
for (int i = 0; i <6; i++) {
final int index = i;
fixedThreadPool.execute(() -> {
try {
//休眠 3 秒
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " index:" + index);
});
}
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("4秒后...");
//關閉線程池后,已提交的任務仍然會執行完
fixedThreadPool.shutdown();
}
}
打印結果:
pool-1-thread-2 index:1
pool-1-thread-3 index:2
pool-1-thread-1 index:0
4秒后...
pool-1-thread-1 index:4
pool-1-thread-3 index:5
pool-1-thread-2 index:3
2. newCachedThreadPool,創建可緩存的線程池,如果線程池的容量超過了任務數,自動回收空閑線程,任務增加時可以自動添加新線程,線程池的容量不限制。
package constxiong.concurrency.a011;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 測試創建可緩存的線程池
* @author ConstXiong
*/
public class TestNewCachedThreadPool {
public static void main(String[] args) {
//創建可緩存的線程池,如果線程池的容量超過了任務數,自動回收空閑線程,任務增加時可以自動添加新線程,線程池的容量不限制
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i <6; i++) {
final int index = i;
cachedThreadPool.execute(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " index:" + index);
});
}
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("4秒后...");
cachedThreadPool.shutdown();
}
}
打印結果可以看出,創建的線程數與任務數相等
pool-1-thread-1 index:0
pool-1-thread-3 index:2
pool-1-thread-6 index:5
pool-1-thread-4 index:3
pool-1-thread-5 index:4
pool-1-thread-2 index:1
4秒后...
3. newScheduledThreadPool,創建定長線程池,可執行周期性的任務。
package constxiong.concurrency.a011;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 測試創建定長線程池,可執行周期性的任務
* @author ConstXiong
*/
public class TestNewScheduledThreadPool {
public static void main(String[] args) {
//創建定長線程池,可執行周期性的任務
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
for (int i = 0; i <3; i++) {
final int index = i;
//scheduleWithFixedDelay 固定的延遲時間執行任務; scheduleAtFixedRate 固定的頻率執行任務
scheduledThreadPool.scheduleWithFixedDelay(() -> {
System.out.println(Thread.currentThread().getName() + " index:" + index);
}, 0, 3, TimeUnit.SECONDS);
}
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("4秒后...");
scheduledThreadPool.shutdown();
}
}
打印結果:
pool-1-thread-1 index:0
pool-1-thread-3 index:2
pool-1-thread-2 index:1
pool-1-thread-1 index:0
pool-1-thread-2 index:1
pool-1-thread-3 index:2
4秒后...
4. newSingleThreadExecutor,創建單線程的線程池,線程異常結束,會創建一個新的線程,能確保任務按提交順序執行。
package constxiong.concurrency.a011;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 測試單線程的線程池
* @author ConstXiong
*/
public class TestNewSingleThreadExecutor {
public static void main(String[] args) {
//單線程的線程池,線程異常結束,會創建一個新的線程,能確保任務按提交順序執行
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
//提交 3 個任務
for (int i = 0; i <3; i++) {
final int index = i;
singleThreadPool.execute(() -> {
//執行第二個任務時,報錯,測試線程池會創建新的線程執行任務三
if (index == 1) {
throw new RuntimeException("線程執行出現異常");
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " index:" + index);
});
}
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("4秒后...");
singleThreadPool.shutdown();
}
}
打印結果可以看出,即使任務出現了異常,線程池還是會自動補充一個線程繼續執行下面的任務
pool-1-thread-1 index:0
Exception in thread "pool-1-thread-1"
java.lang.RuntimeException: 線程執行出現異常
at constxiong.concurrency.a011.TestNewSingleThreadExecutor.lambda$0(TestNewSingleThreadExecutor.java:21)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
4秒后...
pool-1-thread-2 index:2
5. newSingleThreadScheduledExecutor,創建單線程可執行周期性任務的線程池。
package constxiong.concurrency.a011;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 測試單線程可執行周期性任務的線程池
* @author ConstXiong
*/
public class TestNewSingleThreadScheduledExecutor {
public static void main(String[] args) {
//創建單線程可執行周期性任務的線程池
ScheduledExecutorService singleScheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
//提交 3 個固定頻率執行的任務
for (int i = 0; i <3; i++) {
final int index = i;
//scheduleWithFixedDelay 固定的延遲時間執行任務; scheduleAtFixedRate 固定的頻率執行任務
singleScheduledThreadPool.scheduleAtFixedRate(() -> {
System.out.println(Thread.currentThread().getName() + " index:" + index);
}, 0, 3, TimeUnit.SECONDS);
}
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("4秒后...");
singleScheduledThreadPool.shutdown();
}
}
打印機結果可以看出 0-2 任務都被執行了 2 個周期
pool-1-thread-1 index:0
pool-1-thread-1 index:1
pool-1-thread-1 index:2
pool-1-thread-1 index:0
pool-1-thread-1 index:1
pool-1-thread-1 index:2
4秒后...
6. newWorkStealingPool,創建任務可竊取線程池,空閑線程可以竊取其他任務隊列的任務,不保證執行順序,適合任務耗時差異較大。
package constxiong.concurrency.a011;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 測試可任務竊取線程池
* @author ConstXiong
*/
public class TestNewWorkStealingPool {
public static void main(String[] args) {
//創建 4個工作線程的 任務可竊取線程池,如果不設置並行數,默認取 CPU 總核數
ExecutorService workStealingThreadPool = Executors.newWorkStealingPool(4);
for (int i = 0; i <10; i++) {
final int index = i;
workStealingThreadPool.execute(() -> {
try {
//模擬任務執行時間為 任務編號為0 1 2 的執行時間需要 3秒;其余任務200 毫秒,導致任務時間差異較大
if (index <= 2) {
Thread.sleep(3000);
} else {
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " index:" + index);
});
}
try {
Thread.sleep(10000);//休眠 10 秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("10秒后...");
}
}
打印結果可以看出,線程 ForkJoinPool-1-worker-0 把3-9的任務都執行完
ForkJoinPool-1-worker-0 index:3
ForkJoinPool-1-worker-0 index:4
ForkJoinPool-1-worker-0 index:5
ForkJoinPool-1-worker-0 index:6
ForkJoinPool-1-worker-0 index:7
ForkJoinPool-1-worker-0 index:8
ForkJoinPool-1-worker-0 index:9
ForkJoinPool-1-worker-1 index:0
ForkJoinPool-1-worker-3 index:2
ForkJoinPool-1-worker-2 index:1
10秒后...
- Java 自學指南
- Java 面試題匯總PC端瀏覽【點這里】
- Java知識圖譜
- Java 面試題匯總小程序瀏覽,掃二維碼
所有資源資源匯總於公眾號