线程池执行异常不打印日志

  • Post author:
  • Post category:其他


背景

平时自己在使用的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() 方法。



版权声明:本文为fajing_feiyue原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。