Java线程池中线程异常后:是销毁还是复用?

excute提交:

image-fote.png

execute 提交到线程池的方式,如果执行中抛出异常,并且没有在执行逻辑中catch,那么会抛出异常,并且移除抛出异常的线程,创建新的线程放入到线程池中。

image-lctv.png

image-ncjt.png

可以发现,如果抛出异常,会移除抛出异常的线程,创建新的线程。

submit提交:

image-lrxu.png

submit 提交到线程池的方式,如果执行中抛出异常,并且没有catch,不会抛出异常,不会创建新的线程。

submit也是调用了execute方法,但是在调用之前,包装了一层 RunnableFuture,那一定是在RunnableFuture的实现 FutureTask中有特殊处理了,我们查看源码可以发现

image-fsvu.png

image-ckhd.png

但是,我们通过 java.util.concurrent.FutureTask#get(),就可以获取对应的异常信息。

总结

当一个线程池里面的线程异常后:

  • 当执行方式是execute时,可以看到堆栈异常的输出,线程池会把这个线程移除掉,并创建一个新的线程放到线程池中。
  • 当执行方式是submit时,堆栈异常没有输出。但是调用 Future.get()方法时,可以捕获到异常,不会把这个线程移除掉,也不会创建新的线程放入到线程池中。

附:

Runnable和Callable区别:

  1. 方法不同

Runnable接口只有一个run()方法,该方法不返回任何值,因此无法抛出任何checked Exception。

Callable接口则有一个call()方法,它可以返回一个值,并且可以抛出一个checked Exception。

  1. 返回值不同

Runnable的run()方法没有返回值,只是一个void类型的方法。

Callable的call()方法却必须有一个返回值,并且返回值的类型可以通过泛型进行指定。

  1. 异常处理不同

在Runnable中,我们无法对run()方法抛出的异常进行任何处理。

但在Callable中,自定义的call()方法可以抛出一个checked Exception,并由其执行者Handler进行捕获并处理。