第一句子网 - 唯美句子、句子迷、好句子大全
第一句子网 > Java多线程:捕获线程异常

Java多线程:捕获线程异常

时间:2021-03-17 02:58:03

相关推荐

Java多线程:捕获线程异常

你处理过多线程中的异常吗?如何捕获多线程中发生的异常?捕获子线程的异常与捕获当前线程的异常一样简单吗?

除了try catch。Java中还可以通过异常处理器UncaughtExceptionHandler来处理那些未捕获的异常。

# 在当前线程捕获当前线程发生的异常:

/***@authorfutao*@date/6/17*/@Slf4jpublicclassExceptionInCurThread{publicstaticvoidmain(String[]args){try{thrownewRuntimeException("在主线程抛出异常,在主线程捕获");}catch(RuntimeExceptione){log.error("捕获到异常",e);}}}

结果:

结论:在当前线程通过try catch可以捕获当前线程抛出的异常。

# 可以在当前通过try catch的方式捕获其他线程抛出的异常吗?'

/***@author喜欢天文的pony站长*Createdon/6/16.*/publicclassExceptionInChildThreadimplementsRunnable{@Overridepublicvoidrun(){thrownewRuntimeException("子线程发生了异常...");}/***模拟子线程发生异常**@throwsInterruptedException*/privatestaticvoidexceptionThread()throwsInterruptedException{newThread(newExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);newThread(newExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);newThread(newExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);newThread(newExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);}/***在主线程尝试通过trycatch捕获异常*/privatestaticvoidcatchInMain(){try{exceptionThread();}catch(Exceptione){//无法捕获发生在其他线程中的异常log.error("捕获到了异常?",e);}}publicstaticvoidmain(String[]args)throwsInterruptedException{ExceptionInChildThread.catchInMain();}}

(错误的)预期:

在运行第一个线程的时候发生了异常,被catch捕获,打印捕获到了异常?和异常堆栈且后面的线程将不会运行。

实际运行结果:

并不符合预期。

没有被try catch捕获。

后续的线程没有因为第一个线程发生异常而跳过。

结论:

无法在一个线程中通过try catch捕获另外一个线程的异常。

# 解决方案

在每个线程内部run()方法内通过try catch捕获当前线程发生的异常。

缺点:每个线程都需要编写重复的try catch 代码

使用线程异常处理器UncaughtExceptionHandler

给所有线程设置统一的异常处理器

给每个线程设置特定的异常处理器

给线程组设置异常处理器

给线程池设置异常处理器

因为线程池也是通过new Thread()的方式创建的线程,所以思想与上面两种方法一致。

注意:execute()submit()方式对异常处理的不同。

# 在线程内部run()通过try catch捕获异常

/***@author喜欢天文的pony站长*Createdon/6/16.*/@Slf4jpublicclassExceptionInChildThreadimplementsRunnable{@Overridepublicvoidrun(){try{//dosomethingelse...thrownewRuntimeException("子线程发生了异常...");}catch(Exceptione){log.error("在线程内部捕获异常",e);}}/***模拟子线程发生异常**@throwsInterruptedException*/privatestaticvoidexceptionThread()throwsInterruptedException{newThread(newExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);newThread(newExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);newThread(newExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);newThread(newExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);}/***在主线程尝试通过trycatch捕获异常*/privatestaticvoidcatchInMain(){try{exceptionThread();}catch(Exceptione){//无法捕获发生在其他线程中的异常log.error("捕获到了异常?",e);}}publicstaticvoidmain(String[]args)throwsInterruptedException{ExceptionInChildThread.catchInMain();}}

结果:

成功在子线程内部run()方法捕获到了异常

# 使用线程异常处理器UncaughtExceptionHandler

当一个线程由于未捕获异常而退出时,JVM会把这个事件报告给应用程序提供的UncaughtExceptionHandler异常处理器

自定义线程异常处理器

/***自定义线程未捕获异常处理器**@authorfutao*@date/6/17*/publicclassCustomThreadUncaughtExceptionHandlerimplementsThread.UncaughtExceptionHandler{privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(CustomThreadUncaughtExceptionHandler.class);@OverridepublicvoiduncaughtException(Threadt,Throwablee){LOGGER.error("捕获到线程发生的异常,线程信息:[{}]",JSON.toJSONString(t),e);}}

使用:

1. 全局:

Thread.setDefaultUncaughtExceptionHandler(new CustomThreadUncaughtExceptionHandler());

通过调用Thread的静态方法setDefaultUncaughtExceptionHandler(),设置Thread的静态属性defaultUncaughtExceptionHandler.为我们自定义的异常处理器。

源码:

测试:

/***@author喜欢天文的pony站长*Createdon/6/16.*/@Slf4jpublicclassExceptionInChildThreadimplementsRunnable{@Overridepublicvoidrun(){thrownewRuntimeException("子线程发生了异常...");}/***模拟子线程发生异常**@throwsInterruptedException*/privatestaticvoidexceptionThread()throwsInterruptedException{newThread(newExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);newThread(newExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);newThread(newExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);newThread(newExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);}publicstaticvoidmain(String[]args)throwsInterruptedException{//设置全局的线程异常处理器Thread.setDefaultUncaughtExceptionHandler(newCustomThreadUncaughtExceptionHandler());exceptionThread();}}

结果: 成功捕获

2. 为指定线程设置特定的异常处理器

细心的同学已经发现了,在上面Thread类的截图中,还有一个实例属性private volatile UncaughtExceptionHandler uncaughtExceptionHandler;。通过给这个属性赋值,可以实现为每个线程对象设置不同的异常处理器。

测试使用

/***@author喜欢天文的pony站长*Createdon/6/16.*/@Slf4jpublicclassExceptionInChildThreadimplementsRunnable{@Overridepublicvoidrun(){thrownewRuntimeException("子线程发生了异常...");}/***模拟子线程发生异常**@throwsInterruptedException*/privatestaticvoidexceptionThread()throwsInterruptedException{Threadthread1=newThread(newExceptionInChildThread());//为指定线程设置特定的异常处理器thread1.setUncaughtExceptionHandler(newCustomThreadUncaughtExceptionHandler());thread1.start();TimeUnit.MILLISECONDS.sleep(200L);newThread(newExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);newThread(newExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);newThread(newExceptionInChildThread()).start();TimeUnit.MILLISECONDS.sleep(200L);}publicstaticvoidmain(String[]args)throwsInterruptedException{exceptionThread();}}

结果: 成功捕获线程1的异常信息

3. 线程组

/***@authorfutao*@date/6/20*/@Slf4jpublicclassExceptionInThreadGroupimplementsRunnable{@Overridepublicvoidrun(){thrownewRuntimeException("线程任务发生了异常");}publicstaticvoidmain(String[]args)throwsInterruptedException{ThreadGroupthreadGroup=newThreadGroup("只知道抛出异常的线程组..."){@OverridepublicvoiduncaughtException(Threadt,Throwablee){super.uncaughtException(t,e);log.error("线程组内捕获到线程[{},{}]异常",t.getId(),t.getName(),e);}};ExceptionInThreadGroupexceptionInThreadGroup=newExceptionInThreadGroup();newThread(threadGroup,exceptionInThreadGroup,"线程1").start();TimeUnit.MILLISECONDS.sleep(300L);//优先获取绑定在thread对象上的异常处理器Threadthread=newThread(threadGroup,exceptionInThreadGroup,"线程2");thread.setUncaughtExceptionHandler(newCustomThreadUncaughtExceptionHandler());thread.start();TimeUnit.MILLISECONDS.sleep(300L);newThread(threadGroup,exceptionInThreadGroup,"线程3").start();}}

结果:

4. 线程池

/***@authorfutao*@date/6/17*/publicclassCatchThreadPoolException{publicstaticvoidmain(String[]args){ThreadPoolExecutorthreadPoolExecutor=newThreadPoolExecutor(2,4,1L,TimeUnit.MINUTES,newLinkedBlockingDeque<>(1024),newThreadFactory(){@OverridepublicThreadnewThread(Runnabler){Threadthread=newThread(r);//设置线程异常处理器thread.setUncaughtExceptionHandler(newCustomThreadUncaughtExceptionHandler());returnthread;}});threadPoolExecutor.execute(newRunnable(){@Overridepublicvoidrun(){thrownewRuntimeException("execute()发生异常");}});threadPoolExecutor.submit(newRunnable(){@Overridepublicvoidrun(){thrownewRuntimeException("submit.run()发生异常");}});threadPoolExecutor.submit(newCallable<String>(){@OverridepublicStringcall()throwsException{thrownewRuntimeException("submit.call()发生异常");}});threadPoolExecutor.shutdown();}}

结果: 并不符合预期,预期应该捕获三个异常

只捕获到了通过execute()提交的任务的异常

没有捕获到通过submit()提交的任务的异常

通过afterExecute()捕获submit()任务的异常

通过submit()方法的源码可以发现,submit()是将runnable()封装成了RunnableFuture<Void>,并最终调用execute(ftask);执行。

/***@authorfutao*@date/6/17*/@Slf4jpublicclassCatchThreadPoolException{publicstaticvoidmain(String[]args)throwsInterruptedException,ExecutionException{ThreadPoolExecutorthreadPoolExecutor=newThreadPoolExecutor(2,4,1L,TimeUnit.MINUTES,newLinkedBlockingDeque<>(1024),newThreadFactory(){@OverridepublicThreadnewThread(Runnabler){Threadthread=newThread(r);//设置线程异常处理器thread.setUncaughtExceptionHandler(newCustomThreadUncaughtExceptionHandler());returnthread;}}){/***捕获{@codeFutureTask<?>}抛出的异常**@paramr*@paramt*/@OverrideprotectedvoidafterExecute(Runnabler,Throwablet){super.afterExecute(r,t);if(rinstanceofFutureTask<?>){try{//get()的时候会将异常内的异常抛出((FutureTask<?>)r).get();}catch(InterruptedExceptione){e.printStackTrace();Thread.currentThread().interrupt();}catch(ExecutionExceptione){log.error("捕获到线程的异常返回值",e);}}//Throwablet永远为null,拿不到异常信息//log.error("afterExecute中捕获到异常,", t);}};threadPoolExecutor.execute(newRunnable(){@Overridepublicvoidrun(){thrownewRuntimeException("execute()发生异常");}});TimeUnit.MILLISECONDS.sleep(200L);threadPoolExecutor.submit(newRunnable(){@Overridepublicvoidrun(){thrownewRuntimeException("submit.run()发生异常");}});TimeUnit.MILLISECONDS.sleep(200L);threadPoolExecutor.submit(newCallable<String>(){@OverridepublicStringcall()throwsException{thrownewRuntimeException("submit.call()发生异常");}}).get();//get()的时候会将异常抛出threadPoolExecutor.shutdown();}}

欢迎在评论区留下你看文章时的思考,及时说出,有助于加深记忆和理解,还能和像你一样也喜欢这个话题的读者相遇~

# 本文源代码

/FutaoSmile/learn-thread/tree/master/src/main/java/com/futao/learn/threads/捕获线程异常

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。