java线程/线程池异常处理机制


1、线程的异常处理

1.1、线程异常介绍

先观察下面一段代码

public class ThreadTest {
    public static void main(String[] args) {
        Runnable runnable = () -> {
            System.out.println(5 / 0);
            System.out.println(11111);
        };
        try {
            new Thread(runnable).start();
        } catch (Exception e) {
            System.out.println("外部抓取异常");
            e.printStackTrace();
        }
    }
}

运行此段代码,会抛出一个异常,线程断开。结果如下:

Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
	at com.iscas.biz.test.thread.exception.ThreadTest.lambda$main$0(ThreadTest.java:12)
	at java.base/java.lang.Thread.run(Thread.java:834)

Process finished with exit code 0

可看到异常抛出在Runable内部,而不会被外部的catch抓取到,因为线程是独立执行的代码片断,线程的问题应该由线程自己来解决,在Java中,线程方法的异常都应该在线程代码边界之内进行处理。
查看Runable的源码会发现并没有throws异常,所以无法向外抛出异常。

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

需要注意的是,在线程内部,如果有检查异常,是一定需要catch处理的,不然会编译不过。

在多线程开发中,线程内部出现异常一般需要通过打日志的方式记录异常

public class ThreadTest2 {
    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadTest2.class);

    public static void main(String[] args) {
        Runnable runnable = () -> {
            try {
                System.out.println(5 / 0);
                System.out.println(11111);
            } catch (Exception e) {
                LOGGER.error("执行异常", e);
            }
        };
        new Thread(runnable).start();
    }
}

1.2、线程异常处理器

按照1.1打印日志的方式,每个线程都要编写一段异常处理逻辑,会出现很多重复代码,可以使用Thread的UncaughtExceptionHandler接口实现异常处理。
如下所示:

public class ThreadTest3 {
    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadTest3.class);

    public static void main(String[] args) {
        Runnable runnable1 = () -> {
            System.out.println(5 / 0);
            System.out.println(11111);
        };
        Runnable runnable2 = () -> {
            int[] ints = new int[1];
            System.out.println(ints[2]);
        };
        CustomExceptionHandler customExceptionHandler = new CustomExceptionHandler();
        Thread thread1 = new Thread(runnable1);
        Thread thread2 = new Thread(runnable2);
        thread1.setUncaughtExceptionHandler(customExceptionHandler);
        thread2.setUncaughtExceptionHandler(customExceptionHandler);
        thread1.start();
        thread2.start();
    }

    public static class CustomExceptionHandler implements Thread.UncaughtExceptionHandler {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            LOGGER.error("线程执行异常", e);
        }
    }
}

输出如下:

18:19:21.653 [Thread-1] ERROR com.iscas.biz.test.thread.exception.ThreadTest3 - 线程执行异常
java.lang.ArrayIndexOutOfBoundsException: Index 2 out of bounds for length 1
	at com.iscas.biz.test.thread.exception.ThreadTest3.lambda$main$1(ThreadTest3.java:22) ~[classes/:?]
	at java.lang.Thread.run(Thread.java:834) ~[?:?]
18:19:21.653 [Thread-0] ERROR com.iscas.biz.test.thread.exception.ThreadTest3 - 线程执行异常
java.lang.ArithmeticException: / by zero
	at com.iscas.biz.test.thread.exception.ThreadTest3.lambda$main$0(ThreadTest3.java:17) ~[classes/:?]
	at java.lang.Thread.run(Thread.java:834) ~[?:?]

Process finished with exit code 0

当一个线程由于未捕获异常而退出时,JVM会把这个事件报告给应用程序提供的UncaughtExceptionHandler异常处理器,注意的是如果在Runnable内将异常catch处理了,就不会在进入异常处理器了。

1.3、线程组异常处理

public class ThreadTest5 {
    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadTest5.class);

    public static void main(String[] args) {
        Runnable runnable1 = () -> {
            System.out.println(5 / 0);
            System.out.println(11111);
        };
        ThreadGroup threadGroup = new ThreadGroup("threadGroup") {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                LOGGER.error("全局线程异常处理:线程执行异常", e);
            }
        };

        Thread thread1 = new Thread(threadGroup, runnable1);
        thread1.start();
    }
}

执行结果:

19:14:19.188 [Thread-0] ERROR com.iscas.biz.test.thread.exception.ThreadTest5 - 全局线程异常处理:线程执行异常
java.lang.ArithmeticException: / by zero
	at com.iscas.biz.test.thread.exception.ThreadTest5.lambda$main$0(ThreadTest5.java:17) ~[classes/:?]
	at java.lang.Thread.run(Thread.java:834) ~[?:?]

Process finished with exit code 0

1.4、默认全局线程异常处理器

还可以设置一个全局的异常处理,如果没有在线程内部catch处理,也没有为线程定义UncaughtExceptionHandler,会应用此全局处理。

public class ThreadTest4 {
    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadTest4.class);

    public static void main(String[] args) {
        Runnable runnable1 = () -> {
            System.out.println(5 / 0);
            System.out.println(11111);
        };
        Runnable runnable2 = () -> {
            int[] ints = new int[1];
            System.out.println(ints[2]);
        };
        Thread.setDefaultUncaughtExceptionHandler(new CustomExceptionHandler2());
        CustomExceptionHandler customExceptionHandler = new CustomExceptionHandler();
        Thread thread1 = new Thread(runnable1);
        Thread thread2 = new Thread(runnable2);
        thread1.setUncaughtExceptionHandler(customExceptionHandler);
        thread1.start();
        thread2.start();
    }

    public static class CustomExceptionHandler implements Thread.UncaughtExceptionHandler {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            LOGGER.error("线程执行异常", e);
        }
    }

    public static class CustomExceptionHandler2 implements Thread.UncaughtExceptionHandler {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            LOGGER.error("全局线程异常处理:线程执行异常", e);
        }
    }
}

执行结果:

19:09:01.687 [Thread-0] ERROR com.iscas.biz.test.thread.exception.ThreadTest4 - 线程执行异常
java.lang.ArithmeticException: / by zero
	at com.iscas.biz.test.thread.exception.ThreadTest4.lambda$main$0(ThreadTest4.java:17) ~[classes/:?]
	at java.lang.Thread.run(Thread.java:834) ~[?:?]
19:09:01.687 [Thread-1] ERROR com.iscas.biz.test.thread.exception.ThreadTest4 - 全局线程异常处理:线程执行异常
java.lang.ArrayIndexOutOfBoundsException: Index 2 out of bounds for length 1
	at com.iscas.biz.test.thread.exception.ThreadTest4.lambda$main$1(ThreadTest4.java:22) ~[classes/:?]
	at java.lang.Thread.run(Thread.java:834) ~[?:?]

Process finished with exit code 0

2、线程池异常处理

2.1、线程池execute方法

线程池使用execute方法如果未catch非检查异常,会显示异常,跟单独使用Thread的情况差不多,

public class ThreadPoolTest1 {
    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolTest1.class);

    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 3000, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(), new ThreadPoolExecutor.AbortPolicy());
        AtomicReference<Thread> threadAtomicReference = new AtomicReference<>();
        executor.execute(() -> {
            threadAtomicReference.set(Thread.currentThread());
            System.out.println("线程内部:" + threadAtomicReference.get().getState());
            System.out.println(4 / 0);
        });
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("线程外部:" + threadAtomicReference.get().getState());

    }
}

输出:

线程内部:RUNNABLE
Exception in thread "pool-2-thread-1" java.lang.ArithmeticException: / by zero
	at com.iscas.biz.test.thread.exception.ThreadPoolTest1.lambda$main$0(ThreadPoolTest1.java:27)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:834)
线程外部:TERMINATED

要记录或处理这种情况线程的异常情况,采用以下几种方式

2.1.1、单独catch处理

public class ThreadPoolTest1 {
    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolTest1.class);

    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 3000, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(), new ThreadPoolExecutor.AbortPolicy());
        AtomicReference<Thread> threadAtomicReference = new AtomicReference<>();
        executor.execute(() -> {
            threadAtomicReference.set(Thread.currentThread());
            System.out.println("线程内部:" + threadAtomicReference.get().getState());
            try {
                System.out.println(4 / 0);
            } catch (Exception e) {
                LOGGER.error("执行异常", e);
            }
        });
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("线程外部:" + threadAtomicReference.get().getState());

    }
}

输出:

线程内部:RUNNABLE
19:32:26.907 [pool-2-thread-1] ERROR com.iscas.biz.test.thread.exception.ThreadPoolTest1 - 执行异常
java.lang.ArithmeticException: / by zero
	at com.iscas.biz.test.thread.exception.ThreadPoolTest1.lambda$main$0(ThreadPoolTest1.java:28) ~[classes/:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
	at java.lang.Thread.run(Thread.java:834) ~[?:?]
线程外部:WAITING

这样处理会有个问题,不管有没有抛出异常都需要在Runnable内将代码块catch起来,因为可能会存在非检查异常,如果不catch会漏掉日志。
这里显示watting是因为任务执行完毕,线程池中会保持核心线程池的个数处于等待状态 waiting。

2.1.2、继承ThreadPoolExecutor重写afterExecute方法

public class ThreadPoolTest2 {
    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolTest2.class);

    public static void main(String[] args) {
        ThreadPoolExecutor executor = new CustomThreadPoolExecutor(1, 2, 3000, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(), new ThreadPoolExecutor.AbortPolicy());
        AtomicReference<Thread> threadAtomicReference = new AtomicReference<>();
        executor.execute(() -> {
            threadAtomicReference.set(Thread.currentThread());
            System.out.println("线程内部:" + threadAtomicReference.get().getState());
            System.out.println(4 / 0);
        });
        try {
            TimeUnit.SECONDS.sleep(15);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("线程外部:" + threadAtomicReference.get().getState());

    }

    public static class CustomThreadPoolExecutor extends ThreadPoolExecutor {

        public CustomThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        }

        public CustomThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue, @NotNull ThreadFactory threadFactory) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
        }

        public CustomThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue, @NotNull RejectedExecutionHandler handler) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
        }

        public CustomThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue, @NotNull ThreadFactory threadFactory, @NotNull RejectedExecutionHandler handler) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
        }

        @Override
        protected void afterExecute(Runnable r, Throwable t) {
            super.afterExecute(r, t);
            LOGGER.error("执行异常", t);
        }
    }
}

输出:

线程内部:RUNNABLE
19:40:20.094 [pool-2-thread-1] ERROR com.iscas.biz.test.thread.exception.ThreadPoolTest2 - 执行异常
java.lang.ArithmeticException: / by zero
	at com.iscas.biz.test.thread.exception.ThreadPoolTest2.lambda$main$0(ThreadPoolTest2.java:26) ~[classes/:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
	at java.lang.Thread.run(Thread.java:834) ~[?:?]
Exception in thread "pool-2-thread-1" java.lang.ArithmeticException: / by zero
	at com.iscas.biz.test.thread.exception.ThreadPoolTest2.lambda$main$0(ThreadPoolTest2.java:26)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:834)
线程外部:TERMINATED

2.1.3、使用Thread.setUncaughtExceptionHandler方法设置异常处理器

使用线程池构造器中的线程工厂设置异常处理器

public class ThreadPoolTest3 {
    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolTest3.class);

    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 3000, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(), new ThreadFactory() {
            @Override
            public Thread newThread(@NotNull Runnable r) {
                Thread thread = new Thread(r);
                thread.setName("customFactory-");
                thread.setUncaughtExceptionHandler(new CustomExceptionHandler());
                return thread;
            }
        }, new ThreadPoolExecutor.AbortPolicy());
        AtomicReference<Thread> threadAtomicReference = new AtomicReference<>();
        executor.execute(() -> {
            threadAtomicReference.set(Thread.currentThread());
            System.out.println("线程内部:" + threadAtomicReference.get().getState());
            System.out.println(4 / 0);
        });
        try {
            TimeUnit.SECONDS.sleep(15);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("线程外部:" + threadAtomicReference.get().getState());

    }

    public static class CustomExceptionHandler implements Thread.UncaughtExceptionHandler {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            LOGGER.error("线程执行异常", e);
        }
    }
}

输出:

线程内部:RUNNABLE
19:48:05.045 [customFactory-] ERROR com.iscas.biz.test.thread.exception.ThreadPoolTest3 - 线程执行异常
java.lang.ArithmeticException: / by zero
	at com.iscas.biz.test.thread.exception.ThreadPoolTest3.lambda$main$0(ThreadPoolTest3.java:34) ~[classes/:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
	at java.lang.Thread.run(Thread.java:834) ~[?:?]
线程外部:TERMINATED

2.1.4、使用ThreadGroup重写uncaughtException方法

public class ThreadPoolTest4 {
    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolTest4.class);

    public static void main(String[] args) {
        ThreadGroup threadGroup = new ThreadGroup("threadGroup"){
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                LOGGER.error("执行异常", e);
            }
        };
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 3000, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(), r -> new Thread(threadGroup, r), new ThreadPoolExecutor.AbortPolicy());
        AtomicReference<Thread> threadAtomicReference = new AtomicReference<>();
        executor.execute(() -> {
            threadAtomicReference.set(Thread.currentThread());
            System.out.println("线程内部:" + threadAtomicReference.get().getState());
            System.out.println(4 / 0);
        });
        try {
            TimeUnit.SECONDS.sleep(15);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("线程外部:" + threadAtomicReference.get().getState());

    }

}

输出:

线程内部:RUNNABLE
19:52:10.599 [Thread-0] ERROR com.iscas.biz.test.thread.exception.ThreadPoolTest4 - 执行异常
java.lang.ArithmeticException: / by zero
	at com.iscas.biz.test.thread.exception.ThreadPoolTest4.lambda$main$1(ThreadPoolTest4.java:33) ~[classes/:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
	at java.lang.Thread.run(Thread.java:834) ~[?:?]
线程外部:TERMINATED

2.2、线程池submit方法

线程池如果调用的时submit方法,会返回一个Futrue,如果不进行任何处理,不会显示异常。

public class ThreadPoolSubmitTest1 {
    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolSubmitTest1.class);

    public static void main(String[] args) {

        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 3000, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(), new ThreadPoolExecutor.AbortPolicy());
        AtomicReference<Thread> threadAtomicReference = new AtomicReference<>();
        executor.submit(() -> {
            threadAtomicReference.set(Thread.currentThread());
            System.out.println("线程内部:" + threadAtomicReference.get().getState());
            System.out.println(4 / 0);
        });
        try {
            TimeUnit.SECONDS.sleep(15);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("线程外部:" + threadAtomicReference.get().getState());

    }

这种情况有两种方式,一种是在线程内部catch,一种是通过Futrue#get()进行异常catch处理

2.2.1 catch处理

public class ThreadPoolSubmitTest2 {
    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolSubmitTest2.class);

    public static void main(String[] args) {

        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 3000, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(), new ThreadPoolExecutor.AbortPolicy());
        AtomicReference<Thread> threadAtomicReference = new AtomicReference<>();
        executor.submit(() -> {
            threadAtomicReference.set(Thread.currentThread());
            try {
                System.out.println("线程内部:" + threadAtomicReference.get().getState());
                System.out.println(4 / 0);
            } catch (Exception e) {
                LOGGER.error("执行异常", e);
            }
        });
        try {
            TimeUnit.SECONDS.sleep(15);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("线程外部:" + threadAtomicReference.get().getState());

    }

}

输出

线程内部:RUNNABLE
19:58:41.961 [pool-2-thread-1] ERROR com.iscas.biz.test.thread.exception.ThreadPoolSubmitTest2 - 执行异常
java.lang.ArithmeticException: / by zero
	at com.iscas.biz.test.thread.exception.ThreadPoolSubmitTest2.lambda$main$0(ThreadPoolSubmitTest2.java:29) ~[classes/:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[?:?]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
	at java.lang.Thread.run(Thread.java:834) ~[?:?]
线程外部:WAITING

2.2.2 Futrue#get处理

public class ThreadPoolSubmitTest3 {
    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolSubmitTest3.class);

    public static void main(String[] args) {

        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 3000, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(), new ThreadPoolExecutor.AbortPolicy());
        AtomicReference<Thread> threadAtomicReference = new AtomicReference<>();
        Future<?> future = executor.submit(() -> {
            threadAtomicReference.set(Thread.currentThread());
            System.out.println("线程内部:" + threadAtomicReference.get().getState());
            System.out.println(4 / 0);
        });
        try {
            future.get();
        } catch (Exception e) {
            LOGGER.error("执行异常", e);
        }
        try {
            TimeUnit.SECONDS.sleep(15);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("线程外部:" + threadAtomicReference.get().getState());

    }

}

输出

线程内部:RUNNABLE
20:00:53.102 [main] ERROR com.iscas.biz.test.thread.exception.ThreadPoolSubmitTest3 - 执行异常
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
	at java.util.concurrent.FutureTask.report(FutureTask.java:122) ~[?:?]
	at java.util.concurrent.FutureTask.get(FutureTask.java:191) ~[?:?]
	at com.iscas.biz.test.thread.exception.ThreadPoolSubmitTest3.main(ThreadPoolSubmitTest3.java:29) ~[classes/:?]
Caused by: java.lang.ArithmeticException: / by zero
	at com.iscas.biz.test.thread.exception.ThreadPoolSubmitTest3.lambda$main$0(ThreadPoolSubmitTest3.java:26) ~[classes/:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[?:?]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
	at java.lang.Thread.run(Thread.java:834) ~[?:?]
线程外部:WAITING

注意哈,与Runnable对应的Callable内的call的接口有throws异常。

3、定时任务线程池异常处理

需要注意,一旦ScheduledExecutorService执行的逻辑中抛出异常,那么调度会自动停止,并且不会有任何提示信息。在其scheduleWithFixedDelay()和scheduleAtFixedRate()方法都是如此。

这种方式不能使用异常处理器处理,可以使用catch处理

public class ThreadPoolSchedulerTest2 {
    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolSchedulerTest2.class);
    public static void main(String[] args) {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
        executor.scheduleAtFixedRate(() -> {
            try {
                System.out.println(111);
                System.out.println(4 / 0);
            } catch (Exception e) {
                LOGGER.error("执行出错", e);
            }
        }, 1, 2, TimeUnit.SECONDS);
    }
}

输出

111
20:31:38.173 [pool-2-thread-2] ERROR com.iscas.biz.test.thread.exception.ThreadPoolSchedulerTest2 - 执行出错
java.lang.ArithmeticException: / by zero
	at com.iscas.biz.test.thread.exception.ThreadPoolSchedulerTest2.lambda$main$0(ThreadPoolSchedulerTest2.java:25) ~[classes/:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[?:?]
	at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305) ~[?:?]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
	at java.lang.Thread.run(Thread.java:834) ~[?:?]
111
20:31:40.178 [pool-2-thread-2] ERROR com.iscas.biz.test.thread.exception.ThreadPoolSchedulerTest2 - 执行出错
java.lang.ArithmeticException: / by zero
	at com.iscas.biz.test.thread.exception.ThreadPoolSchedulerTest2.lambda$main$0(ThreadPoolSchedulerTest2.java:25) ~[classes/:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[?:?]
	at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305) ~[?:?]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
	at java.lang.Thread.run(Thread.java:834) ~[?:?]

注意的是使用Future#get处理异常,定时任务不会继续执行,测试如下:

public class ThreadPoolSchedulerTest1 {
    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolSchedulerTest1.class);

    public static void main(String[] args) {

        ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
        ScheduledFuture<?> future = executor.scheduleAtFixedRate(() -> {
            System.out.println(1111);
            System.out.println(4 / 0);
        }, 1, 2, TimeUnit.SECONDS);
        try {
            future.get();
        } catch (Exception e) {
            LOGGER.error("执行异常", e);
        }

    }

}

输出:

1111
20:32:51.159 [main] ERROR com.iscas.biz.test.thread.exception.ThreadPoolSchedulerTest1 - 执行异常
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
	at java.util.concurrent.FutureTask.report(FutureTask.java:122) ~[?:?]
	at java.util.concurrent.FutureTask.get(FutureTask.java:191) ~[?:?]
	at com.iscas.biz.test.thread.exception.ThreadPoolSchedulerTest1.main(ThreadPoolSchedulerTest1.java:28) ~[classes/:?]
Caused by: java.lang.ArithmeticException: / by zero
	at com.iscas.biz.test.thread.exception.ThreadPoolSchedulerTest1.lambda$main$0(ThreadPoolSchedulerTest1.java:25) ~[classes/:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[?:?]
	at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305) ~[?:?]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
	at java.lang.Thread.run(Thread.java:834) ~[?:?]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值