如何在線程池中找到堆棧

先看一個示例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));
    }
}

運行結果如下:
在這裏插入圖片描述
可以發現,現在通過異常堆棧信息,除了能知道是哪裏拋出了異常,還能夠知道是哪個地方提交了這個任務,才導致的這個異常,而知道是哪個地方調用導致了這個異常,就能夠跟進到這個地方,做相應的代碼分析和修改了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章