更多精彩文章。java
这么多监控组件,总有一款适合你bash
《程序员画像,十年沉浮》post
最有用系列:spa
《Linux生产环境上,最经常使用的一套“vim“技巧》线程
《Linux生产环境上,最经常使用的一套“Sed“技巧》code
若是你认同这些知识,欢迎关注微信公众号小姐姐味道
ID:xjjdog
假设咱们有一个线程池,因为程序须要,咱们向该线程池中提交了好多好多任务,可是 这些任务都没有对异常进行try catch处理,而且运行的时候都抛出了异常 。这会对线程池的运行带来什么影响?
正确答案是:没有影响。 这可不是好事情。
想一下,若是是你开发了一个线程池供开发者使用,你会不会对这种状况作处理?想一想也是确定的,否则你提供给别人使用的东西就是有问题的,欠考虑的。并且java线程池的主要开发人员是大名鼎鼎的Doug Lea,你以为他开发的代码怎么会容许出现这种问题?
这个问题很棘手,由于它躺在角落里,程序正常运行的时候,它并不会出来做祟。
接下来咱们来看一下java中的线程池是如何运行咱们提交的任务的,详细流程比较复杂,这里咱们不关注,咱们只关注任务执行的部分。java中的线程池用的是ThreadPoolExecutor,真正执行代码的部分是runWorker方法:final void runWorker(Worker w)
能够看到,程序会捕获包括Error在内的全部异常,而且在程序最后,将出现过的异常和当前任务传递给afterExecute方法。
而ThreadPoolExecutor中的afterExecute方法是没有任何实现的。
protected void afterExecute(Runnable r, Throwable t) { }
复制代码
想象下ThreadPoolExecutor这种处理方式会有什么问题?
这样作可以保证咱们提交的任务抛出了异常不会影响其余任务的执行,同时也不会对用来执行该任务的线程产生任何影响。
问题就在afterExecute方法上, 这个方法没有作任何处理,因此若是咱们的任务抛出了异常,咱们也没法马上感知到。 即便感知到了,也没法查看异常信息。
因此,做为一名好的开发者,是不该该容许这种状况出现的。
思路很简单。
一、在提交的任务中将异常捕获并处理,不抛给线程池。
二、异常抛给线程池,可是咱们要及时处理抛出的异常。
第一种思路很简单,就是咱们提交任务的时候,将全部可能的异常都Catch住,而且本身处理。
说白了就是把业务逻辑都trycatch起来。 可是这种思路的缺点就是: 1)全部的不一样任务类型都要trycatch,增长了代码量。
2)不存在checkedexception的地方也须要都trycatch起来,代码丑陋。
第二种思路就能够避免上面的两个问题。
第二种思路又有如下四种实现方式
自定义线程池,继承ThreadPoolExecutor并复写其afterExecute(Runnable r, Throwable t)方法。
实现Thread.UncaughtExceptionHandler接口,实现void uncaughtException(Thread t, Throwable e);方法,并将该handler传递给线程池的ThreadFactory
覆盖其uncaughtException方法。(与第二种方式相似,由于ThreadGroup类自己就实现了Thread.UncaughtExceptionHandler接口)
尤为注意:上面三种方式针对的都是经过execute(xx)的方式提交任务,若是你提交任务用的是submit()方法,那么上面的三种方式都将不起做用,而应该使用下面的方式
若是提交任务的时候使用的方法是submit,那么该方法将返回一个Future对象,全部的异常以及处理结果均可以经过future对象获取。 采用Future模式,将返回结果以及异常放到Future中,在Future中处理
异常处理是java中很是重要的流程,可是线程池的默认操做,会使的这些内容被静悄悄的忽略,这在某些状况下是致命的。
文章探讨了从用户层面的代码到线程池层面的各类改造方法,力求让业务代码更加健壮可控。