Java线程池注意事项之异常处理

问题现象

在使用线程池的过程中,线程池中的工作线程发生异常,没有看到抛出,在控制台和日志里也没有打印错误日志,看不到任何蛛丝马迹,就仿佛没有发生异常一样。但是实际上确确实实发生了异常。

那么究竟是什么原因导致异常被吞掉?

问题验证

单个线程的验证

在验证线程池之前,我们先验证一下单个线程内部发生异常的情况,看它是否会抛出异常。

线程可以由 Runnable 和 Callable 来构造,所以我们先各自创建一个线程类。如下:

public static class SinkRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(1 / 0);
        System.out.println("test!");
        // 应当处理线程池中的线程的内部异常,避免无从得知是否发生异常
        /*try {
            System.out.println(1 / 0);
            System.out.println("test!");
        } catch (Exception e) {
            System.err.println(e);
        }*/
    }
}

public static class SinkCallable implements Callable {

    @Override
    public Object call() {
        System.out.println(1 / 0);
        System.out.println("test!");
        // 同上,应当处理线程池中的线程的内部异常,避免无从得知是否发生异常
        return "rtn";
    }
} 
  

接着我们分别启动两种线程:

public static void main(String[] args) {
    // case1:Runnable会抛出异常
    // new SinkRunnable().run();

    // case2:Callable会抛出异常
    // new SinkCallable().call();
}

【结论】:Runnable 和 Callable 都会抛出异常。

线程池的验证

接着创建线程池,使用线程池的方式验证工作线程是否会抛出异常。

public static void main(String[] args) {
    // case3:submit不会抛出异常,不论submit的是Runnable还是Callable
    // ExecutorService pool = Executors.newFixedThreadPool(4);
    // pool.submit(new SinkRunnable());

    // case4:execute会抛出异常,execute的只能是Runnable
    ExecutorService pool = Executors.newFixedThreadPool(4);
    pool.execute(new SinkRunnable());
}

【结论】:submit 不会抛出异常,不论 submit 的是 Runnable 还是 Callable;execute 会抛出异常,execute 的只能是 Runnable。

解决方案

要注意线程池采用 submit 的方式提交线程后,一定要手动处理线程内部的异常!

另外,submit 的话,调用 Future.get() 方法时,可以捕获到异常。

线程池中工作线程的异常,不会影响线程池里面其他线程的正常执行。线程池会把这个线程移除掉,并创建一个新的线程放到线程池中。

你可能感兴趣的:(J2SE)