去年年底,我正在运行预定的任务来监视Neo4j集群,而我遇到的问题之一是有时监视会退出。
我最终意识到这是因为RuntimeException被抛出到Runnable方法中,而我没有处理它。 以下代码演示了该问题:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;public class RunnableBlog {public static void main(String[] args) throws ExecutionException, InterruptedException {ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();executor.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " -> " + System.currentTimeMillis());throw new RuntimeException("game over");}}, 0, 1000, TimeUnit.MILLISECONDS).get();System.out.println("exit");executor.shutdown();}
}
如果运行该代码,我们将看到RuntimeException,但是执行器不会退出,因为线程在没有通知的情况下就死了:
Exception in thread "main" pool-1-thread-1 -> 1391212558074
java.util.concurrent.ExecutionException: java.lang.RuntimeException: game overat java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:252)at java.util.concurrent.FutureTask.get(FutureTask.java:111)at RunnableBlog.main(RunnableBlog.java:11)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:601)at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: java.lang.RuntimeException: game overat RunnableBlog$1.run(RunnableBlog.java:16)at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:351)at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:178)at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)at java.lang.Thread.run(Thread.java:722)
当时我最终添加了一个try catch块并打印如下异常:
public class RunnableBlog {public static void main(String[] args) throws ExecutionException, InterruptedException {ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();executor.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {try {System.out.println(Thread.currentThread().getName() + " -> " + System.currentTimeMillis());throw new RuntimeException("game over");} catch (RuntimeException e) {e.printStackTrace();}}}, 0, 1000, TimeUnit.MILLISECONDS).get();System.out.println("exit");executor.shutdown();}
}
据我所知,这允许异常被识别,并且执行Runnable的线程不会死亡。
java.lang.RuntimeException: game over
pool-1-thread-1 -> 1391212651955at RunnableBlog$1.run(RunnableBlog.java:16)at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:351)at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:178)at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)at java.lang.Thread.run(Thread.java:722)
pool-1-thread-1 -> 1391212652956
java.lang.RuntimeException: game overat RunnableBlog$1.run(RunnableBlog.java:16)at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:351)at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:178)at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)at java.lang.Thread.run(Thread.java:722)
pool-1-thread-1 -> 1391212653955
java.lang.RuntimeException: game overat RunnableBlog$1.run(RunnableBlog.java:16)at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:351)at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:178)at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)at java.lang.Thread.run(Thread.java:722)
这很好,使我能够继续监视集群。
但是,我最近开始阅读“ Java Concurrency in Practice ”(购买后仅6年!),并意识到这可能不是处理RuntimeException的正确方法。
public class RunnableBlog {public static void main(String[] args) throws ExecutionException, InterruptedException {ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();executor.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {try {System.out.println(Thread.currentThread().getName() + " -> " + System.currentTimeMillis());throw new RuntimeException("game over");} catch (RuntimeException e) {Thread t = Thread.currentThread();t.getUncaughtExceptionHandler().uncaughtException(t, e);}}}, 0, 1000, TimeUnit.MILLISECONDS).get();System.out.println("exit");executor.shutdown();}
}
我认为这两种方法之间没有太大区别,所以如果有人可以向我解释为什么这种方法比我以前的捕获异常并打印堆栈跟踪的方法更好的话,那将是一个很好的选择。
参考: Java:通过Mark Needham博客博客的JCG合作伙伴 Mark Needham 处理可运行的RuntimeException 。
翻译自: https://www.javacodegeeks.com/2014/02/java-handling-a-runtimeexception-in-a-runnable.html