執行緒遇到未處理的例外就結束了
這個好理解,當執行緒出現未捕獲例外的時候就執行不下去了,留給它的就是垃圾回收了,
執行緒池中執行緒頻繁出現未捕獲例外
當執行緒池中執行緒頻繁出現未捕獲的例外,那執行緒的復用率就大大降低了,需要不斷地創建新執行緒,
做個實驗:
public class ThreadExecutor {
private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(200), new ThreadFactoryBuilder().setNameFormat("customThread %d").build());
@Test
public void test() {
IntStream.rangeClosed(1, 5).forEach(i -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadPoolExecutor.execute(() -> {
int j = 1/0;
});});
}
}
新建一個只有一個執行緒的執行緒池,每隔0.1s提交一個任務,任務中是一個1/0的計算,
Exception in thread "customThread 0" java.lang.ArithmeticException: / by zero
at thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "customThread 1" java.lang.ArithmeticException: / by zero
at thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "customThread 2" java.lang.ArithmeticException: / by zero
at thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "customThread 3" java.lang.ArithmeticException: / by zero
at thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "customThread 4" java.lang.ArithmeticException: / by zero
at thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "customThread 5" java.lang.ArithmeticException: / by zero
at thread.ThreadExecutor.lambda$null$0(ThreadExecutor.java:25)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:748)
可見每次執行的執行緒都不一樣,之前的執行緒都沒有復用,原因是因為出現了未捕獲的例外,
我們把例外捕獲試試:
public class ThreadExecutor {
private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(200), new ThreadFactoryBuilder().setNameFormat("customThread %d").build());
@Test
public void test() {
IntStream.rangeClosed(1, 5).forEach(i -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadPoolExecutor.execute(() -> {
try {
int j = 1 / 0;
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() +" "+ e.getMessage());
}
});
});
}
}
customThread 0 / by zero
customThread 0 / by zero
customThread 0 / by zero
customThread 0 / by zero
customThread 0 / by zero
可見當例外捕獲了,執行緒就可以復用了,
問題來了,我們的代碼中例外不可能全部捕獲
如果要捕獲那些沒被業務代碼捕獲的例外,可以設定Thread類的uncaughtExceptionHandler屬性,這時使用ThreadFactoryBuilder會比較方便,ThreadFactoryBuilder是guava提供的ThreadFactory生成器,
new ThreadFactoryBuilder()
.setNameFormat("customThread %d")
.setUncaughtExceptionHandler((t, e) -> System.out.println(t.getName() + "發生例外" + e.getCause()))
.build()
修改之后:
public class ThreadExecutor {
private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(200),
new ThreadFactoryBuilder()
.setNameFormat("customThread %d")
.setUncaughtExceptionHandler((t, e) -> System.out.println("UncaughtExceptionHandler捕獲到:" + t.getName() + "發生例外" + e.getMessage()))
.build());
@Test
public void test() {
IntStream.rangeClosed(1, 5).forEach(i -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadPoolExecutor.execute(() -> {
System.out.println("執行緒" + Thread.currentThread().getName() + "執行");
int j = 1 / 0;
});
});
}
}
執行緒customThread 0執行
UncaughtExceptionHandler捕獲到:customThread 0發生例外/ by zero
執行緒customThread 1執行
UncaughtExceptionHandler捕獲到:customThread 1發生例外/ by zero
執行緒customThread 2執行
UncaughtExceptionHandler捕獲到:customThread 2發生例外/ by zero
執行緒customThread 3執行
UncaughtExceptionHandler捕獲到:customThread 3發生例外/ by zero
執行緒customThread 4執行
UncaughtExceptionHandler捕獲到:customThread 4發生例外/ by zero
可見,結果并不是我們想象的那樣,執行緒池中原有的執行緒沒有復用!所以通過UncaughtExceptionHandler想將例外吞掉使執行緒復用這招貌似行不通,它只是做了一層例外的保底處理,
將excute改成submit試試
public class ThreadExecutor {
private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(200),
new ThreadFactoryBuilder()
.setNameFormat("customThread %d")
.setUncaughtExceptionHandler((t, e) -> System.out.println("UncaughtExceptionHandler捕獲到:" + t.getName() + "發生例外" + e.getMessage()))
.build());
@Test
public void test() {
IntStream.rangeClosed(1, 5).forEach(i -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Future<?> future = threadPoolExecutor.submit(() -> {
System.out.println("執行緒" + Thread.currentThread().getName() + "執行");
int j = 1 / 0;
});
try {
future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
}
}
執行緒customThread 0執行
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
執行緒customThread 0執行
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
執行緒customThread 0執行
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
執行緒customThread 0執行
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
執行緒customThread 0執行
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
通過submit提交執行緒可以屏蔽執行緒中產生的例外,達到執行緒復用,當get()執行結果時例外才會拋出,
原因是通過submit提交的執行緒,當發生例外時,會將例外保存,待future.get();時才會拋出,
這是Futuretask的部分run()方法,看setException:
public void run() {
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
}
}
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
將例外存在outcome物件中,沒有拋出,再看get方法:
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
當outcome是例外時才拋出,
總結
1、執行緒池中執行緒中例外盡量手動捕獲
2、通過設定ThreadFactory的UncaughtExceptionHandler可以對未捕獲的例外做保底處理,通過execute提交任務,執行緒依然會中斷,而通過submit提交任務,可以獲取執行緒執行結果,執行緒例外會在get執行結果時拋出,
本文鏈接:https://blog.csdn.net/weixin_37968613/article/details/108407774
著作權宣告:本文為博主原創文章,遵循 CC 4.0 BY-SA 著作權協議,轉載請附上原文出處鏈接和本宣告,
近期熱文推薦:
1.1,000+ 道 Java面試題及答案整理(2021最新版)
2.別在再滿屏的 if/ else 了,試試策略模式,真香!!
3.臥槽!Java 中的 xx ≠ null 是什么新語法?
4.Spring Boot 2.5 重磅發布,黑暗模式太炸了!
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/310692.html
標籤:Java
