java執行緒池中任務異常處理
阿新 • • 發佈:2019-01-22
首先我們看個例子,當使用執行緒池執行任務時如果某個任務出現異常會是什麼效果
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class DivTask implements Runnable {
int a,b;
@Override
public void run() {
System.out.println(a/b);
}
public DivTask(int a, int b) {
super();
this.a = a;
this.b = b;
}
public static void main(String[] args) {
ThreadPoolExecutor pools=new ThreadPoolExecutor(0, 10, 0,TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
for(int i=0;i<5;i++){
pools.submit(new DivTask(100,i));
}
}
}
執行結果
100
50
25
你會發現少了一個結果輸出,這個缺失的原因是由於i=0導致的,但是系統卻一點錯誤提示都沒有,後面如果使用在複雜的業務場景中,那麼出現這種錯誤去排查起來可就困難了
解決辦法
1. 將sumit()方法換成execute()執行,即pools.execute(new DivTask(100,i));而且一個任務的異常對其他任務的執行不會造成任何影響
2. 使用改造submit(),使用future.get()獲取異常資訊
Future future=pools.submit(new DivTask(100 ,i));
future.get();
執行結果如下
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:188)
at threadpool.DivTask.main(DivTask.java:26)
Caused by: java.lang.ArithmeticException: / by zero
at threadpool.DivTask.run(DivTask.java:15)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
我們發現異常資訊雖能正常打印出來,可仔細看,卻發現異常卻發現只知道異常是從哪裡丟擲,不知道這個產生異常的任務是從哪裡提交的,任務的具體提交位置已經被執行緒池完全淹沒了,我們只能看到執行緒池的排程流程,這對於我們排查錯誤造成很大的困難。
如何詳細的打印出執行緒池中提交的任務所丟擲的異常資訊
1. 在提交的任務中將異常捕獲,不拋給執行緒池
//其實就是將所有的業務邏輯都使用try...catch起來,但是這種辦法感覺很low,程式碼量增加不少,不推薦
@Override
public void run() {
try {
//處理所有的業務邏輯
} catch (Throwable e) {
//列印日誌等
} finally {
//其他處理
}
}
2.自定義執行緒池,execute()、submit()方法,在呼叫這些方法前,對任務進行一次包裝然後再提交,實際上也是第一種方法的思路,只是不用在每個run()都去try catch處理異常
public static void main(String[] args) throws InterruptedException, ExecutionException {
ThreadPoolExecutor pools=new ThreadPoolExecutor(0, 10, 0,TimeUnit.SECONDS,new SynchronousQueue<Runnable>()){
private Runnable warp(final Runnable task,final Exception clientStack, String clientThreadName){
return new Runnable() {
@Override
public void run() {
try {
task.run();
} catch (Exception e) {
clientStack.printStackTrace();
throw e;
}
}
};
}
private Exception clientTrace(){
return new Exception("client stack trace");
}
@Override
public void execute(Runnable command) {
// TODO Auto-generated method stub
super.execute(warp(command,clientTrace(), Thread.currentThread().getName()));
}
@Override
public Future<?> submit(Runnable task) {
return super.submit(warp(task,clientTrace(),Thread.currentThread().getName()));
}
};
for(int i=0;i<5;i++){
pools.execute(new DivTask(100,i));
}
}
執行結果
100
33
25
50
java.lang.Exception: client stack trace
at threadpool.DivTask$1.clientTrace(DivTask.java:39)
at threadpool.DivTask$1.execute(DivTask.java:44)
at threadpool.DivTask.main(DivTask.java:52)
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
at threadpool.DivTask.run(DivTask.java:16)
at threadpool.DivTask$1$1.run(DivTask.java:30)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
為什麼現在能定位出異常任務是在哪裡提交的,關鍵點就在於我們在submit()中傳入了一個自定義的Exception。一旦任務有異常,我們就會將此異常丟擲,這樣我們就能定位是哪裡submit的了.