線程池執行異常不打印日誌

背景


平時自己在使用的ThreadPoolExecutor的時候,提交任務用submit和execute方法用的比較隨意,知道當需要獲取返回結果的時候用submit。但當並不需要結果的時候submit和execute用得比較隨意。在一次使用submit的時候並沒有獲得預期結果,但也沒有異常日誌輸出。在進行一波調試之後,任務在線程中出現異常了,但也並未出現異常拋出的情況。

代碼測試


1、先用execute 查看當出現異常的情況

public class ThreadPoolTest {



    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 10L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(500),
            new ThreadFactory() {
                @Override
                public Thread newThread(Runnable r) {
                    Thread thread = new Thread(r);
                    thread.setName("submit-execute-test");
                    return thread;
                }


            }
    );

    private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss SSS");

    public static void main(String[] args) {
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            int j = i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(2 + random.nextInt(5));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (j % 2 != 0) {
                        throw new RuntimeException("線程錯誤");
                    }
                    LinkedBlockingQueue<Runnable> queue = (LinkedBlockingQueue) executor.getQueue();
                    System.out.println(formatter.format(LocalDateTime.now()) + "::" + Thread.currentThread().getName() + "::" +
                            "核心線程數 :" + executor.getCorePoolSize() +
                            "::最大線程數 :" + executor.getMaximumPoolSize() +
                            "::活動線程數 :" + executor.getActiveCount() +
                            "::任務完成數" + executor.getCompletedTaskCount() +
                            "::隊列使用 :" + queue.size() + "::隊列未使用 :" + queue.remainingCapacity() + "::隊列總共大小 :" + (queue.size() + queue.remainingCapacity()));


                }
            });

        }
    }
}

在這裏插入圖片描述
從打印中可以看到當execute提交runable的時候異常會輸出。

2、我們將execute 換成submit ,其他都不變,再次查看運行情況
在這裏插入圖片描述

可以看出除了我們在代碼裏面輸出內容並未拋出異常。

原因


查看ThreadPoolExecutor 的submit方法,是將Runable方法進行封裝,再交給Executor去execute,這個方法和ThreadPoolExecutor 的execute方法一樣

  public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

newTaskFor 方法是創建FutureTask 對象,那麼具體不拋異常應該就在FutureTask的run方法了。

  protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }

FutureTask 的run方法,在run方法中對Throwable 異常進行捕獲,錯誤的時候將異常設置到返回結果。執行正常結束,將返回結果set到結果中

 public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        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);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

建議


根據上面的分析,如果ThreadPoolExecutor 提交任務的時候如果並不關心返回結果最好直接使用ThreadPoolExecutor.execute() 方法。

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