先看一個示例demo
有一個線程任務DivTask,如下:
/**
* 除法任務
*/
public class DivTask implements Runnable {
int a, b;
public DivTask(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public void run() {
double c = a / b;
System.out.println(c);
}
}
測試方法:
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
for (int i = 0; i < 5; i++) {
executorService.execute(new DivTask(100, i));
}
}
運行結果如下:
可以發現DivTask的run方法做的是除法,所以當除數爲0時,就會拋出相應異常,但是通過上圖的異常信息,只能夠知道是哪裏拋出了異常,卻不能夠知道,是哪個地方提交了這個任務,才導致的這個異常,由於一個任務完全有可能被多處調用,這就造成了異常定位困難了。
自定義一個線程池,讓它在調度任務前,先保存一下提交任務線程的堆棧信息,如下:
import java.util.concurrent.*;
public class StackForThreadPool extends ThreadPoolExecutor {
public StackForThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
/**
* 自定義一個異常
*
* @param threadName
* @return
*/
private Exception getTrace(String threadName) {
return new Exception(threadName + "【Stack Trace Info】");
}
/**
* 對線程任務再做一層包裝
*
* @param task
* @param stack
* @return
*/
private Runnable wrap(final Runnable task, final Exception stack) {
return () -> {
try {
task.run();
} catch (Exception e) {
stack.printStackTrace();
throw e;
}
};
}
/**
* 重寫execute()方法
*
* @param command
*/
@Override
public void execute(Runnable command) {
super.execute(wrap(command, getTrace(Thread.currentThread().getName())));
}
/**
* 重寫submit()方法
*
* @param task
* @return
*/
@Override
public Future<?> submit(Runnable task) {
return super.submit(wrap(task, getTrace(Thread.currentThread().getName())));
}
}
測試方法:
public static void main(String[] args) {
ExecutorService executorService = new StackForThreadPool(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
for (int i = 0; i < 5; i++) {
executorService.execute(new DivTask(100, i));
}
}
運行結果如下:
可以發現,現在通過異常堆棧信息,除了能知道是哪裏拋出了異常,還能夠知道是哪個地方提交了這個任務,才導致的這個異常,而知道是哪個地方調用導致了這個異常,就能夠跟進到這個地方,做相應的代碼分析和修改了。