shutdown和shutdownNow方法的區(qū)別
- shutdown => 平緩關(guān)閉,等待所有已添加到線程池中的任務(wù)執(zhí)行完在關(guān)閉
- shutdownNow => 立刻關(guān)閉茸时,停止正在執(zhí)行的任務(wù)盟迟,并返回隊列中未執(zhí)行的任務(wù)
shutdown和shutdownNow方法的優(yōu)缺點
關(guān)閉方法 | 安全性 | 響應(yīng)性 |
---|---|---|
shutdown | 高 | 低 |
shutdownNow | 低 | 高 |
通過表格一對比就可以知道shutdown和shutdownNow方法的優(yōu)缺點端姚,shutdown雖然安全设褐,但是響應(yīng)性不高,shutdownNow方法雖然響應(yīng)性高但不安全泡挺,在項目中選擇使用哪種方法關(guān)閉線程池需要進(jìn)行權(quán)衡
如何記錄shutdownNow方法關(guān)閉線程池未完成的任務(wù)
因為shutdownNow方法會立刻停止執(zhí)行中的任務(wù)辈讶,如果不記錄未完成的任務(wù),將會造成任務(wù)的丟失娄猫。使用shutdownNow方法關(guān)閉任務(wù)需要記錄兩部分任務(wù):
- 隊列中尚未執(zhí)行的任務(wù)
- 關(guān)閉時正在執(zhí)行的任務(wù)
隊列中尚未執(zhí)行的任務(wù)調(diào)用shutdownNow方法就會返回贱除。記錄關(guān)閉時正在執(zhí)行的任務(wù)需要在execute方法中判斷此時線程池是否關(guān)閉,如果關(guān)閉了將記錄媳溺,實現(xiàn)該功能需要重寫execute方法
Executor和ExecutorService為接口月幌,AbstractExecutorServcie為實現(xiàn)類。在Executor類中有一個execute方法悬蔽,重寫execute方法就是寫一個類繼承AbstractExecutorService
AbstractExecutorService繼承類:TrackingExecutor.java
public class TrackingExecutor extends AbstractExecutorService {
private static ExecutorService es;
/**
* 同步set,存放未完成的任務(wù)
* */
private static Set<Runnable> tasksCancelledAtShutdown = Collections.synchronizedSet(new HashSet<>());
public TrackingExecutor(ExecutorService es) {
this.es = es;
}
public List<Runnable> getCancelledTasks() {
if (!es.isTerminated()) {
throw new IllegalStateException();
}
return new ArrayList<>(tasksCancelledAtShutdown);
}
@Override
public void execute(Runnable command) {
es.execute(new Runnable() {
@Override
public void run() {
try {
command.run();
} finally {
if (isShutdown() && Thread.currentThread().isInterrupted()) {
tasksCancelledAtShutdown.add(command);
}
}
}
});
}
@Override
public void shutdown() {
es.shutdownNow();
}
@Override
public List<Runnable> shutdownNow() {
return es.shutdownNow();
}
@Override
public boolean isShutdown() {
return es.isShutdown();
}
@Override
public boolean isTerminated() {
return es.isTerminated();
}
@Override
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
return es.awaitTermination(timeout, unit);
}
}
說明:
- 重寫executor方法本質(zhì)就是對提交的Runnable進(jìn)行封裝扯躺,使用try-finally代碼塊在finally中判斷線程池是否被關(guān)閉,線程是否被中斷屯阀,條件成立則將當(dāng)前任務(wù)記錄下來
- 為什么判斷線程池關(guān)閉以后仍需判斷當(dāng)前線程是否中斷 => 因為shutdownNow方法底層調(diào)用的仍是interrup方法缅帘,如果該任務(wù)是不可中斷的,那么shutdownNow方法對該任務(wù)的關(guān)閉是無效的难衰,該任務(wù)會一直執(zhí)行
TrackingExecutor使用:TrackingExecutorService
public class TrackingExecutorService {
private TrackingExecutor trackingExecutor = new TrackingExecutor(Executors.newFixedThreadPool(3));
private List<Runnable> runnableList;
public void start() {
//添加10個任務(wù)
for (int i = 0; i < 5; i++) {
trackingExecutor.execute(new Task());
}
}
public void stop() {
//立刻關(guān)閉線程池
runnableList = trackingExecutor.shutdownNow();
try {
if (trackingExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
for (Runnable runnable : trackingExecutor.getCancelledTasks()) {
runnableList.add(runnable);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public List<Runnable> getRunnableList() {
return runnableList;
}
/**
* 自定義任務(wù)
* */
private class Task implements Runnable {
@Override
public void run() {
long start = System.currentTimeMillis();
while (true) {
//執(zhí)行一分鐘
if (System.currentTimeMillis() - start > 1000 * 60) {
break;
}
}
}
}
}
說明:
- start方法 => 往線程池中提交任務(wù)
- stop方法 => 關(guān)閉線程池并記錄未完成的任務(wù),未完成的任務(wù)來自兩部分
- stop方法中awaitTermination方法的使用 => 調(diào)用shutdownNow方法后線程池的停止可能需要一些時間逗栽,因此阻塞等待線程池關(guān)閉盖袭,調(diào)用shutdownNow關(guān)閉線程池成功后該方法將返回true
- Task類 => 自定義類,強制run方法至少執(zhí)行一分鐘,為了使關(guān)閉線程池時仍有任務(wù)未完成
測試類:Test.java
public class Test {
public static void main(String[] args) {
TrackingExecutorService trackingExecutorService = new TrackingExecutorService();
trackingExecutorService.start();
trackingExecutorService.stop();
trackingExecutorService.getRunnableList().stream().forEach(i -> System.out.println("Runnable unfinished: " + i));
}
}
執(zhí)行結(jié)果:
com.h2t.study.concurrent.TrackingExecutor$1@13969fbe
com.h2t.study.concurrent.TrackingExecutor$1@6aaa5eb0
缺點:
記錄的任務(wù)可能已經(jīng)完成了但仍進(jìn)行了記錄鳄虱,因為沒有提供API判斷任務(wù)的執(zhí)行狀態(tài)
最后附:完整代碼
附往期文章:歡迎你的閱讀弟塞、點贊、評論
并發(fā)相關(guān)
1.為什么阿里巴巴要禁用Executors創(chuàng)建線程池拙已?
2.自己的事情自己做决记,線程異常處理
設(shè)計模式相關(guān):
1. 單例模式,你真的寫對了嗎倍踪?
2. (策略模式+工廠模式+map)套餐 Kill 項目中的switch case
JAVA8相關(guān):
1. 使用Stream API優(yōu)化代碼
2. 親系宫,建議你使用LocalDateTime而不是Date哦
數(shù)據(jù)庫相關(guān):
1. mysql數(shù)據(jù)庫時間類型datetime、bigint建车、timestamp的查詢效率比較
2. 很高興扩借!終于踩到了慢查詢的坑
高效相關(guān):
1. 擼一個Java腳手架,一統(tǒng)團(tuán)隊項目結(jié)構(gòu)風(fēng)格
日志相關(guān):
1. 日志框架缤至,選擇Logback Or Log4j2潮罪?
2. Logback配置文件這么寫,TPS提高10倍
工程相關(guān):
1. 閑來無事领斥,動手寫一個LRU本地緩存
2. Redis實現(xiàn)點贊功能模塊
3. JMX可視化監(jiān)控線程池
4. 權(quán)限管理 【SpringSecurity篇】
5. Spring自定義注解從入門到精通
6. java模擬登陸優(yōu)酷
7. QPS這么高嫉到,那就來寫個多級緩存吧
8. java使用phantomjs進(jìn)行截圖
其他:
1. 使用try-with-resources優(yōu)雅關(guān)閉資源
2. 老板,用float存儲金額為什么要扣我工資