添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
import




    
 java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class FinallyTest {
    private static Object str = null;
    public static void main(String[] args) {
        TestRun testRun = new TestRun();
        ExecutorService executor = Executors.newCachedThreadPool();
        Future<?> future = executor.submit(testRun);
        try {
            //future.get(5, TimeUnit.SECONDS);
        }catch (Throwable t){
          	System.out.println("run 方法的异常被 主线程 捕获了。");
            t.printStackTrace();
        System.out.println("main方法结束");
    public static class TestRun implements Runnable {
        public void run() {
            System.out.println(str.toString());

运行结果:

main方法结束
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class FinallyTest {
    private static Object str = null;
    public static void main(String[] args) {
        TestRun testRun = new TestRun();
        ExecutorService executor = Executors.newCachedThreadPool();
        Future<?> future = executor.submit(testRun);
        try {
            future.get(5, TimeUnit.SECONDS);
        }catch (Throwable t){
          	System.out.println("run 方法的异常被 主线程 捕获了。");
            t.printStackTrace();
        System.out.println("main方法结束");
    public static class TestRun implements Runnable {
        public void run() {
            System.out.println(str.toString());

运行结果:

run 方法的异常被 主线程 捕获了。
java.util.concurrent.ExecutionException: java.lang.NullPointerException
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:206)
	at com.zph.study.base.jvm.FinallyTest.main(FinallyTest.java:16)
Caused by: java.lang.NullPointerException
	at com.zph.study.base.jvm.FinallyTest$TestRun.run(FinallyTest.java:27)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
	at java.util.concurrent.FutureTask.run(FutureTask.java)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
main方法结束
原因是什么呢?

因为调用 submit 之后,run 方法抛出的异常被线程池捕获并封装到了 Future 的 get 方法上。代码 1 并没有调用 Future.get ,这样异常只是被捕获了,并没有进一步抛出来,就像被线程池吃掉了一样。而 代码 2 调用了 Future.get ,get 方法将 run 方法抛出的异常进一步抛给了调用 get 方法的地方,所以这里就能够打印出 run 方法的异常信息了。

再看两段使用 submit 的代码

import lombok.SneakyThrows;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class FinallyTest {
    private static Object str = null;
    public static void main(String[] args) {
        TestRun testRun = new TestRun();
        ExecutorService executor = Executors.newCachedThreadPool();
        Future<?> future = executor.submit(testRun);
        try {
            //future.get(5, TimeUnit.SECONDS);
        }catch (Throwable




    
 t){
            System.out.println("run 方法的异常被 主线程 捕获了。");
            t.printStackTrace();
        System.out.println("main方法结束");
    public static class TestRun implements Runnable {
        @SneakyThrows
        public void run() {
            try {
                System.out.println(str.toString());
            }catch (Exception e){
                System.out.println("run 方法的 catch 输出。");
                e.printStackTrace();
                System.out.println("run 方法结束,并抛出异常");
                throw e;

运行结果:

main方法结束
run 方法的 catch 输出。
java.lang.NullPointerException
	at com.zph.study.base.jvm.FinallyTest$TestRun.run(FinallyTest.java:30)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
	at java.util.concurrent.FutureTask.run(FutureTask.java)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
run 方法结束,并抛出异常
import lombok.SneakyThrows;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class FinallyTest {
    private static Object str = null;
    public static void main(String[] args) {
        TestRun testRun = new TestRun();
        ExecutorService executor = Executors.newCachedThreadPool();
        Future<?> future = executor.submit(testRun);
        try {
            future.get(5, TimeUnit.SECONDS);
        }catch (Throwable t){
            System.out.println("run 方法的异常被 主线程 捕获了。");
            t.printStackTrace();
        System.out.println("main方法结束");
    public static class TestRun implements Runnable {
        @SneakyThrows
        public void run() {
            try {
                System.out.println(str.toString());
            }catch (Exception e){
                System.out.println("run 方法的 catch 输出。");
                e.printStackTrace();
                System.out.println("run 方法结束,并抛出异常");
                throw e;

运行结果:

run 方法的 catch 输出。
java.lang.NullPointerException
	at com.zph.study.base.jvm.FinallyTest$TestRun.run(FinallyTest.java:30)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
	at java.util.concurrent.FutureTask.run(FutureTask.java)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
run 方法结束,并抛出异常
run 方法的异常被 主线程 捕获了。
java.util.concurrent.ExecutionException: java.lang.NullPointerException
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:206)
	at com.zph.study.base.jvm.FinallyTest.main(FinallyTest.java:17)
Caused by: java.lang.




    
NullPointerException
	at com.zph.study.base.jvm.FinallyTest$TestRun.run(FinallyTest.java:30)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
	at java.util.concurrent.FutureTask.run(FutureTask.java)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
main方法结束
原因是什么呢?

代码 3 的运行结果我们可以看到,当 run 方法发生异常时,主线程已经结束了。而 代码 4 的运行结果则恰好相反,run 方法结束并抛出异常之后,主线程才捕获到异常,然后主线程结束。这就说明了 Future.get 会阻塞调用者的线程,直到通过 submit 提交的任务的 run 方法执行结束之后才会放开调用 Future.get 的线程。这也就是说,Future.get 将两个并行执行的线程变成了顺序执行。所以与其说线程池是将 run 方法抛出的异常捕获并封装到了 Future 的 get 方法上。倒不如说是调用了 Future.get 之后,调用者线程就一直在等待 run 方法执行完毕,如果 run 方法抛出了异常,就经由 Future.get 方法传递给调用者。

再来看 2 段使用 execute 的代码

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FinallyTest {
    private static Object str = null;
    public static void main(String[] args) {
        TestRun testRun = new TestRun();
        ExecutorService executor = Executors.newCachedThreadPool();
        executor.execute(testRun);
        try {//这个 try 代码块显得很无用,但我放在这里只是想和下边的代码做一个对照
        }catch (Throwable t){
            System.out.println("run 方法的异常被 主线程 捕获了。");
            t.printStackTrace();
        System.out.println("main方法结束");
    public static class TestRun implements Runnable {
        public void run() {
            System.out.println(str.toString());

运行结果:

main方法结束
Exception in thread "pool-1-thread-1" java.lang.NullPointerException
	at com.zph.study.base.jvm.FinallyTest$TestRun.run(FinallyTest.java:24)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FinallyTest {
    private static Object str = null;
    public static void main(String[] args) {
        TestRun testRun = new TestRun();
        ExecutorService executor = Executors.newCachedThreadPool();
        try {
            executor.execute(testRun);
        }catch (Throwable t){
            System.out.println("run 方法的异常被 主线程 捕获了。");
            t.printStackTrace();
        System.out.println("main方法结束");
    public static class TestRun implements Runnable {
        public void run() {
            System.out.println(str.toString());

运行结果:

main方法结束
Exception in thread "pool-1-thread-1" java.lang.NullPointerException
	at com.zph.study.base.jvm.FinallyTest$TestRun.run(FinallyTest.java:24)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
原因是什么呢?

可以看到这两段代码的执行结果完全一样,即使把 execute 的调用放在 try 块中,run 方法抛出的异常也不会被这里的 catch 块捕获到。原因是调用 execute 方法时,线程池并不会对 run 方法抛出的异常进行捕获和处理,异常直接被抛给了 jvm ,jvm 就自己打印出来了。而且放入线程池中执行的 run 方法是异步执行的,很可能在主线程结束的时候,run 方法才刚刚抛出异常,这就更不可能被主线程捕获了。

submit 和 execute 的区别

ExecutorService 的 submit 和 execute 两个方法刚好互补。

  • submit 是无法自动打印异常,因为它将 run 方法抛出的异常捕获后封装到了 Future 的 get 方法上

    在主线程中调用 Future.get 就能捕获到 run 方法中抛出的异常

    • Future.get 会阻塞调用者的线程,直到通过 submit 提交的任务的 run 方法执行结束之后才会放开调用 Future.get 的线程。这也就是说,Future.get 将两个并行执行的线程变成了顺序执行。所以与其说线程池是将 run 方法抛出的异常捕获并封装到了 Future 的 get 方法上。倒不如说是调用了 Future.get 之后,调用者线程就一直在等待 run 方法执行完毕,如果 run 方法抛出了异常,就经由 Future.get 方法传递给调用者。
    • 如果不调用 Future.get 的话,run 方法抛出的异常就像被线程池吃掉了一样,有时候会让你摸不着头脑。明明 run方法抛出异常了,而你的控制台却没有任何异常打印,原因就出在了这里。
    public interface Future<V> {
         * @throws CancellationException if the computation was cancelled
         * @throws ExecutionException if the computation threw an
         * exception
         * @throws InterruptedException if the current thread was interrupted
         * while waiting
         * @throws TimeoutException if the wait timed out
        V get(long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException;
    
  • execute 恰好相反,它在 run 方法抛出异常后直接交给 jvm 打印了,由于 run 方法是异步方法,所以在

    主线程中就无法捕获到 run 方法抛出的异常了

本人属于菜鸟一个,如果有哪些地方写的不对的,希望各位大佬予以指正,谢谢了。

Java通过Executors提供四种线程池,分别为:   newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。   newFixedThreadPool 创建一个定长线程池,可控制线程大并发数,超出的线程会在队列中等待。   newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。   newSingleThreadExecutor 创建一个单线程化的线程池,它只会用的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。   (1) n 前言今天遇到了一个bug,现象是,一个任务放入线程池中,似乎“没有被执行”,日志也没有打。经过本地代码调试之后,发现在任务逻辑的前半段,抛出了NPE,但是代码外层没有try-catch,导致这个异常掉。这个问题解决起来是很简单的,外层加个try-catch就好了,但是这个异常如果没有被catch,线程池内部逻辑是怎么处理这个异常的呢?这个异常最后会跑到哪里呢?带着疑问和好奇心,我研究了一下线程... 先来看一段代码: ExecutorService executorService = Executors.newFixedThreadPool(1); executorService.submit(() -> { System.out.println("任务开始执行..."); int a = 10 / 0; // 抛出异常 executorService.shutdown(); 很明显,我们提交的任务会抛. 在使用ScheduledThreadPoolExecutor执行一个定时任务时,发现执行一次后,任务就不再执行。 代码举例: private static final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1); private static final CountDownLatch countDownLatch = public static void main(String[ 线程池技术是服务器端开发中常用的技术。不论是直接还是间接,各种服务器端功能的执行总是离不开线程池的调度。关于线程池的各种文章,多数是关注任务的创建和执行方面,对于异常处理和任务取消(包括线程池关闭)关注的偏少。 接下来,本文将从 Java 原生线程、两种主要线程池 ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 这三方面介绍 Java 中线程的异常处理机制。 二、Thread 在谈线程池异常处理之前,我们先来看 Java 中线程中的异常是如何被处 今天遇到了一个bug,现象是,一个任务放入线程池中,似乎“没有被执行”,日志也没有打。 经过本地代码调试之后,发现在任务逻辑的前半段,抛出了NumberFormatException,但是代码外层没有try-catch,导致这个异常掉。 这个问题解决起来是很简单的,外层加个try-catch就好了,但是这个异常如果没有被catch,线程池内部逻辑是怎么处理这个异常的呢?这个异常最后会跑到...