请教各位 java 线程池 出现异常的问题! 问题如下: 为啥会出现异常??

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了请教各位 java 线程池 出现异常的问题! 问题如下: 为啥会出现异常??相关的知识,希望对你有一定的参考价值。

package text1.thread;
public class LiftOff implements Runnable
protected int countDown = 10;
private static int taskCount = 0;
private final int id = taskCount++;
public LiftOff()

public String status()
return "#" + id + "(" + (countDown > 0 ? countDown : "发射!") + "),";

@Override
public void run()
while(countDown-- > 0)
System.out.println(status());

Thread.yield();


---------------------------------------------
package text1.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CachedThreadPool
/**
* @param args
*/
public static void main(String[] args)
ExecutorService exec = Executors.newCachedThreadPool();
for(int i = 0; i < 5; i++)
exec.execute(new LiftOff());
exec.shutdown();



--------------------------------------------
Exception in thread "main" java.util.concurrent.RejectedExecutionException
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1760)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:767)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:658)
at text1.thread.CachedThreadPool.main(CachedThreadPool.java:15)
#0(9),
#0(8),
#0(7),
#0(6),
#0(5),
#0(4),
#0(3),
#0(2),
#0(1),
#0(发射!),

package text1.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CachedThreadPool
/**
* @param args
*/
public static void main(String[] args)
ExecutorService exec = Executors.newCachedThreadPool();
for(int i = 0; i < 5; i++)
exec.execute(new LiftOff());
//exec.shutdown(); 这句有问题 不能每次执行后就把线程池关闭了啊。这样就只能执行一个任务,其他任务都被拒绝了。 改在执行完后再关闭池。

exec.shutdown();

应该是你的exec.shutdown();这句位置问题,不能放在循环的内部。

参考技术A 把这句:Thread.yield();去掉试试

Java线程池异常处理的正确姿势

假设我们有一个线程池,由于程序需要,我们向该线程池中提交了好多好多任务,但是 这些任务都没有对异常进行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方法上, 这个方法没有做任何处理,所以如果我们的任务抛出了异常,我们也无法立刻感知到。  即使感知到了,也无法查看异常信息。

所以,作为一名好的开发者,是不应该允许这种情况出现的。


如何避免这种问题

思路很简单。
1、在提交的任务中将异常捕获并处理,不抛给线程池。
2、异常抛给线程池,但是我们要及时处理抛出的异常。

直接catch

第一种思路很简单,就是我们提交任务的时候,将所有可能的异常都Catch住,并且自己处理。
说白了就是把业务逻辑都trycatch起来。
但是这种思路的缺点就是:
1)所有的不同任务类型都要trycatch,增加了代码量。
2)不存在checkedexception的地方也需要都trycatch起来,代码丑陋。

线程池实现

第二种思路就可以避免上面的两个问题。
第二种思路又有以下四种实现方式
自定义线程池
自定义线程池,继承ThreadPoolExecutor并复写其afterExecute(Runnable r, Throwable t)方法。
Java线程池异常处理的正确姿势
实现Thread.UncaughtExceptionHandler接口
实现Thread.UncaughtExceptionHandler接口,
实现void uncaughtException(Thread t, Throwable e);方法,
并将该handler传递给线程池的ThreadFactory

Java线程池异常处理的正确姿势
继承ThreadGroup
覆盖其uncaughtException方法。(与第二种方式类似,因为ThreadGroup类本身就实现了Thread.UncaughtExceptionHandler接口)
尤其注意:上面三种方式针对的都是通过execute(xx)的方式提交任务,如果你提交任务用的是submit()方法,那么上面的三种方式都将不起作用,而应该使用下面的方式
采用Future模式
如果提交任务的时候使用的方法是submit,那么该方法将返回一个Future对象,所有的异常以及处理结果都可以通过future对象获取。
采用Future模式,将返回结果以及异常放到Future中,在Future中处理

总结

文章探讨了从用户层面的代码到线程池层面的各种改造方法,力求让业务代码更加健壮可控。异常处理是java中非常重要的流程,但是线程池的默认操作,会使的这些内容被静悄悄的忽略,这在某些情况下是致命的。

以上是关于请教各位 java 线程池 出现异常的问题! 问题如下: 为啥会出现异常??的主要内容,如果未能解决你的问题,请参考以下文章

JAVA 线程池 其中一个线程执行失败 则线程重新执行或者重新提交任务 急

各位大侠,请教个oracle问题,执行一段代码出现错误,希望各位给出解决办法,谢谢谢!

关于线程池运行过程中,业务逻辑出现未知异常导致线程中断问题反思

面试官:线程池执行过程中遇到异常会发生什么,怎样处理?

请教:java线程问题。创建Thread

Java线程池异常处理的正确姿势