java线程池中任务异常处理

来源:互联网 发布:timeline js 编辑:程序博客网 时间:2024/06/11 23:03

首先我们看个例子,当使用线程池执行任务时如果某个任务出现异常会是什么效果

import java.util.concurrent.SynchronousQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class DivTask implements Runnable {    int a,b;    @Override    public void run() {        System.out.println(a/b);    }    public DivTask(int a, int b) {        super();        this.a = a;        this.b = b;    }    public static void main(String[] args) {        ThreadPoolExecutor pools=new ThreadPoolExecutor(0, 10, 0,TimeUnit.SECONDS,new SynchronousQueue<Runnable>());        for(int i=0;i<5;i++){            pools.submit(new DivTask(100,i));        }    }}运行结果1005025

你会发现少了一个结果输出,这个缺失的原因是由于i=0导致的,但是系统却一点错误提示都没有,后面如果使用在复杂的业务场景中,那么出现这种错误去排查起来可就困难了

解决办法
1. 将sumit()方法换成execute()执行,即pools.execute(new DivTask(100,i));而且一个任务的异常对其他任务的执行不会造成任何影响
2. 使用改造submit(),使用future.get()获取异常信息

Future future=pools.submit(new DivTask(100,i));future.get();运行结果如下Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero    at java.util.concurrent.FutureTask.report(FutureTask.java:122)    at java.util.concurrent.FutureTask.get(FutureTask.java:188)    at threadpool.DivTask.main(DivTask.java:26)Caused by: java.lang.ArithmeticException: / by zero    at threadpool.DivTask.run(DivTask.java:15)    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)    at java.util.concurrent.FutureTask.run(FutureTask.java:262)    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)    at java.lang.Thread.run(Thread.java:744)

我们发现异常信息虽能正常打印出来,可仔细看,却发现异常却发现只知道异常是从哪里抛出,不知道这个产生异常的任务是从哪里提交的,任务的具体提交位置已经被线程池完全淹没了,我们只能看到线程池的调度流程,这对于我们排查错误造成很大的困难。

如何详细的打印出线程池中提交的任务所抛出的异常信息
1. 在提交的任务中将异常捕获,不抛给线程池

//其实就是将所有的业务逻辑都使用try...catch起来,但是这种办法感觉很low,代码量增加不少,不推荐@Override    public void run() {        try {            //处理所有的业务逻辑        } catch (Throwable e) {            //打印日志等        } finally {            //其他处理        }    }

2.自定义线程池,execute()、submit()方法,在调用这些方法前,对任务进行一次包装然后再提交,实际上也是第一种方法的思路,只是不用在每个run()都去try catch处理异常

public static void main(String[] args) throws InterruptedException, ExecutionException {        ThreadPoolExecutor pools=new ThreadPoolExecutor(0, 10, 0,TimeUnit.SECONDS,new SynchronousQueue<Runnable>()){            private Runnable warp(final Runnable task,final Exception clientStack, String clientThreadName){                return new Runnable() {                    @Override                    public void run() {                        try {                            task.run();                        } catch (Exception e) {                            clientStack.printStackTrace();                            throw e;                        }                    }                };            }            private Exception clientTrace(){                return new Exception("client stack trace");            }            @Override            public void execute(Runnable command) {                // TODO Auto-generated method stub                super.execute(warp(command,clientTrace(), Thread.currentThread().getName()));            }            @Override            public Future<?> submit(Runnable task) {                return super.submit(warp(task,clientTrace(),Thread.currentThread().getName()));            }        };        for(int i=0;i<5;i++){            pools.execute(new DivTask(100,i));        }    }

运行结果

100332550java.lang.Exception: client stack trace    at threadpool.DivTask$1.clientTrace(DivTask.java:39)    at threadpool.DivTask$1.execute(DivTask.java:44)    at threadpool.DivTask.main(DivTask.java:52)Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero    at threadpool.DivTask.run(DivTask.java:16)    at threadpool.DivTask$1$1.run(DivTask.java:30)    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)    at java.lang.Thread.run(Thread.java:744)

为什么现在能定位出异常任务是在哪里提交的,关键点就在于我们在submit()中传入了一个自定义的Exception。一旦任务有异常,我们就会将此异常抛出,这样我们就能定位是哪里submit的了.

1 0