如何优雅处理线程池异常?

124 阅读1分钟

1. 看一下几种提交任务API的异常处理结果

    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(1);

        threadPool.submit(() -> {
            throw new RuntimeException("test");
        });

        Future<Object> future = threadPool.submit(() -> {
            throw new RuntimeException("test");
        });

        try {
            future.get();
        } catch (Exception e) {
            System.out.println("future.get() 出现了异常");
        }

        threadPool.execute(()->{
            throw new RuntimeException("test");
        });

        //  future.get() 出现了异常
        //  Exception in thread "pool-1-thread-1" java.lang.RuntimeException: test
        //    at com.hdu.Main.lambda$main$2(Main.java:27)
        //    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        //    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        //    at java.lang.Thread.run(Thread.java:748)
    }

如你所见,sumbit()默认会吞掉异常,除非你主动 get()。这也符合 future的设计逻辑。 对于 execute()来说,他会直接抛出异常。

2. 优雅处理异常

public static void testHandleException() {
        ExecutorService threadPool = new ThreadPoolExecutor(
                Runtime.getRuntime().availableProcessors(),
                Runtime.getRuntime().availableProcessors() * 2,
                1,
                SECONDS,
                new LinkedBlockingQueue<>(100)
        ) {
            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                if (t != null) {
                    System.out.println(t.getMessage());
                }
                if (t == null && r instanceof Future) {
                    try {
                        if (((Future<?>) r).isDone()) {
                            ((Future<?>) r).get();
                        }
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                }
            }
        };

        threadPool.submit(() -> {
            throw new RuntimeException("submitException");
        });

        Future<Object> future = threadPool.submit(() -> {
            throw new RuntimeException("submitAndGetException");
        });

        try {
            future.get();
        } catch (Exception e) {
            System.out.println("future.get() 出现了异常");
        }

        threadPool.execute(() -> {
            throw new RuntimeException("execute Exception");
        });

        // java.lang.RuntimeException: submitException
        // java.lang.RuntimeException: submitAndGetException
        // future.get() 出现了异常
        // execute Exception
        // Exception in thread "pool-1-thread-3" java.lang.RuntimeException: execute Exception
        //    at com.hdu.Main.lambda$testHandleException$2(Main.java:54)
        //    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        //    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        //    at java.lang.Thread.run(Thread.java:748)
    }

重写 afterExecute 逻辑,感知线程池出现异常。

3.说在最后

其实建议在每个任务执行的过程中自己去处理异常,而不是将异常抛给线程池。为什么这么说呢?其实线程池内部如果线程出现了异常都会回收掉出现异常的线程,然后创建新的线程补充上去。这是影响性能的

4.源码

handleThreadPoolException: handleThreadPoolException (gitee.com)