ThreadPoolExecutor類繼承體系:
ThreadPoolExecutor -> AbstractExecutorService -> ExecutorService -> Executor
Executors提供工廠方法累澡,用于創(chuàng)建不同類型的線程池
- newFixedThreadPool()
- newSingleThreadExecutor()
- newCachedThreadPool()
- newScheduledThreadPool()
前兩種線程池可能隊列積壓導(dǎo)致OOM,后兩種可能創(chuàng)建非常多的線程(Integer.MAX_VALUE)碳竟,甚至OOM
以下內(nèi)容借鑒:
【鏈接】如何優(yōu)雅的關(guān)閉Java線程池
http://url.cn/5DGL2xe
線程的狀態(tài)
- NEW
- RUNNABLE(RUNNING)
- BLOCKED
- WAITING
- TIMED_WAIT
- TERMINATED
1. 新建(new) :新創(chuàng)建了一個線程對象译蒂。
2. 可運(yùn)行(runnable) :線程對象創(chuàng)建后,其他線程(比如main線程)調(diào)用了該對象的start()方法拿诸。該狀態(tài)的線程位于可運(yùn)行線程池中愧膀,等待被線程調(diào)度選中呛凶,獲取cpu 的使用權(quán) 旅东。
3. 運(yùn)行(running) :可運(yùn)行狀態(tài)( runnable) 的線程獲得了cpu 時間片(timeslice) 灭抑,執(zhí)行程序代碼。
4. 阻塞(block) :阻塞狀態(tài)是指線程因為某種原因放棄了cpu 使用權(quán)抵代,也即讓出了cpu timeslice腾节,暫時停止運(yùn)行。直到線程進(jìn)入 可運(yùn)行(runnable) 狀態(tài),才有機(jī)會再次獲得cpu timeslice 轉(zhuǎn)到 運(yùn)行(running) 狀態(tài)案腺。阻塞的情況分三種: (一). 等待阻塞: 運(yùn)行(running) 的線程執(zhí)行o.wait()方法庆冕,JVM會把該線程放入等待隊列(waitting queue)中。 (二). 同步阻塞: 運(yùn)行(running) 的線程在獲取對象的同步鎖時劈榨,若該同步鎖被別的線程占用愧杯,則JVM會把該線程放入鎖池(lock pool)中。 (三). 其他阻塞: 運(yùn)行(running) 的線程執(zhí)行Thread.sleep(long ms)或t.join()方法鞋既,或者發(fā)出了I/O請求時,JVM會把該線程置為阻塞狀態(tài)耍铜。當(dāng)sleep()狀態(tài)超時邑闺、join()等待線程終止或者超時、或者I/O處理完畢時棕兼,線程重新轉(zhuǎn)入 可運(yùn)行(runnable) 狀態(tài)陡舅。
5. 死亡(dead) :線程run()、main() 方法執(zhí)行結(jié)束伴挚,或者因異常退出了run()方法靶衍,則該線程結(jié)束生命周期。死亡的線程不可再次復(fù)生茎芋。
執(zhí)行任務(wù)
- execute()
- submit()
可簡單理解為:前者是不需要返回值颅眶,后者需要返回值(Future.get()),雖然都是異步處理田弥。
關(guān)閉線程池
- shutdownNow()
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
1. 拒絕接收新提交的任務(wù)
2. 同時立刻關(guān)閉線程池涛酗,池中的任務(wù)不再執(zhí)行
3. 小結(jié):當(dāng)我們調(diào)用線程池的shutdownNow時,如果線程正在getTask方法中執(zhí)?偷厦,則會通過for循環(huán)進(jìn)入到if語句商叹,于是getTask 返回null,從而線程退出。不管線程池?是否有未完成的任務(wù)只泼。如果線程因為執(zhí)行提交到線程池里的任務(wù)而處于阻塞狀態(tài)剖笙,則會導(dǎo)致報錯。(如果任務(wù)里沒有捕獲InterruptedException異常)请唱,否則線程會執(zhí)行完當(dāng)前任務(wù)弥咪,然后通過getTask方法返回為null來退出。
- shutdown()
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
1. 拒絕接收新提交的任務(wù)
2. 同時等待池中的任務(wù)執(zhí)行完畢后關(guān)閉線程池
3. 小結(jié):當(dāng)我們調(diào)用線程池的shutdownNow時籍滴,如果線程正在getTask方法中執(zhí)?酪夷,則會通過for循環(huán)進(jìn)入到if語句,于是getTask 返回null,從而線程退出孽惰。不管線程池?是否有未完成的任務(wù)晚岭。如果線程因為執(zhí)行提交到線程池里的任務(wù)而處于阻塞狀態(tài),則會導(dǎo)致報錯勋功。(如果任務(wù)里沒有捕獲InterruptedException異常)坦报,否則線程會執(zhí)行完當(dāng)前任務(wù)库说,然后通過getTask方法返回為null來退出。
總結(jié)
- shutdownNow()片择,可能會引起報錯潜的,可能會使線程池關(guān)閉不了,但不一定字管。(isTerminated=false)
- shutdown()啰挪,要保證任務(wù)里不會有永久阻塞等待的邏輯,否則線程池也關(guān)閉不了嘲叔。(isTerminated=false)
TestCase
// 1. 線程正在執(zhí)行時亡呵,shutdownNow不會報錯
final ExecutorService pool1 = Executors.newSingleThreadExecutor();
pool1.submit(() -> {
long startTime = System.currentTimeMillis();
System.out.println("enter the thread pool1");
long counter = 0L;
while (counter < Long.MAX_VALUE >> (Integer.SIZE - 4)) {
Object o = new Object();
o = null;
counter++;
}
System.out.println("weak up from sleeping pool1");
System.out.println("leave the thread pool1");
long finishTime = System.currentTimeMillis();
System.out.println("cost time " + (finishTime - startTime));
});
System.out.println("going to shutdown the pool1");
pool1.shutdownNow();
pool1.awaitTermination(1, TimeUnit.MINUTES);
System.out.println("exit the pool1");
boolean flag = pool1.isTerminated();
System.out.println("pool1->" + flag);
System.out.println("=========================");
// 2. 線程阻塞時,shutdownNow將會拋出InterruptedException
final ExecutorService pool2 = Executors.newSingleThreadExecutor();
pool2.submit(() -> {
System.out.println("enter the thread pool2");
try {
// 為什么兩種不同進(jìn)入阻塞狀態(tài)的實現(xiàn)硫戈,最終結(jié)果不一致
//Thread.currentThread().wait(100_000);
Thread.sleep(100_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("weak up from sleeping pool2");
System.out.println("leave the thread pool2");
});
System.out.println("going to shutdown the pool2");
pool2.shutdownNow();
pool2.awaitTermination(1, TimeUnit.MINUTES);
System.out.println("exit the pool2");
flag = pool2.isTerminated();
System.out.println("pool2->" + flag);
System.out.println("=========================");
// 3. 線程永久阻塞锰什,shutdown將無法關(guān)閉線程池
final ExecutorService pool3 = Executors.newSingleThreadExecutor();
pool3.submit(() -> {
System.out.println("enter the thread pool3");
// for (long l = 0L; l < Long.MAX_VALUE; l++) {
// Object o = new Object();
// o = null;
// }
// try {
// Thread.sleep(1000_000_000); // 模擬永久阻塞
// //Thread.currentThread().wait();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
try {
ServerSocket socket = new ServerSocket(8080);
socket.accept();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("weak up from sleeping pool3");
System.out.println("leave the thread pool3");
});
System.out.println("going to shutdown the pool3");
pool3.shutdownNow();
pool3.awaitTermination(1, TimeUnit.MINUTES);
System.out.println("exit the pool3");
flag = pool3.isTerminated();
System.out.println("pool3->" + flag);