当线程中发生异常时,SchduledExecutorService 不执行 UncaughtExceptionHandler
SchduledExecutorService not executing UncaughtExceptionHandler when exception happens in the thread
我正在使用 ScheduledExecutorService,它使用 ThreadFactory 创建新线程。但是当定时任务出现异常时,threadfactory中的UncaughtExceptionHandler不会被执行。为什么会这样?
线程工厂如下:
public class CustomThreadFactory {
public static ThreadFactory defaultFactory(String namePrefix) {
return new ThreadFactoryBuilder()
.setNameFormat(String.format("%s-%%d", namePrefix))
.setDaemon(false)
.setUncaughtExceptionHandler(new CustomExceptionHandler())
.build();
}
}
异常处理程序:
public class CustomExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread thread, Throwable t) {
log.error("Received uncaught exception {}", thread.getName(), t);
}
}
主要功能:
public static void main(String[] args) {
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(
CustomThreadFactory.defaultFactory("scheduler"));
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 20L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1));
scheduler.scheduleWithFixedDelay(
() -> {
executor.submit(() -> Thread.sleep(10000));
},0,1,TimeUnit.SECONDS);
}
我正在陈述我的确切问题,这样它就不会成为 XY 问题。在上面的代码片段中,阻塞队列的大小为 1,并且每秒都会将任务添加到阻塞队列中。所以在添加第三个任务时,阻塞队列执行器给出 RejectionExecutionException
,它不是由 UncaughtExceptionHandler
.
打印的
当线程由于未捕获的异常而即将终止时,Java 虚拟机将使用 Thread.getUncaughtExceptionHandler() 查询线程的 UncaughtExceptionHandler
并将调用处理程序的 uncaughtException方法,将线程和异常作为参数传递。
UncaughtExceptionHandler 将在提交的任务抛出未捕获的异常但 RejectionExecutionException
没有被任务本身抛出时调用。但是,您可以将 RejectedExecutionHandler
传递给 ThreadPoolExecutor
。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
ThreadPoolExecutor 将 RejectedExecutionHandler
作为参数。
编辑:
在你的情况下 UncaughtExceptionHandler
不是 called/notified 因为当你调用 scheduleWithFixedDelay
时不会直接执行你的 Runnable 但是它会在 ScheduledFutureTask
中扭曲它。现在,当 executor.submit(() -> Thread.sleep(10000));
抛出异常(在您的情况下为 RejectionExecutionException )时,它不会保持未捕获状态,因为 FutureTask#run
捕获了异常并将异常设置为 Future result/outcome。看FutureTask#run
的部分:
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
和setException()
方法将异常对象设置为FutureTask
的outcome
。
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
现在的问题是我们能否以某种方式获得异常的 exception/notification?
是的,这是可能的。当您在 Future 上调用 Future#get
时,将抛出异常,但 Exception
将被包裹在 ExecutionException
中。看看 Future#get
方法。
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
@SuppressWarnings("unchecked")
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
我已经在 ScheduledThreadPoolExecutor
上看到了所有可用的方法(提交、执行、计划等),将 task
包装在未来的任务中。所以我认为没有办法通过 UncaughtExceptionHandler
以防万一。
但是对于 ThreadPoolExecutor,如果您使用 ThreadPoolExecutor#submit
提交任务,您的 UncaughtExceptionHandler
将 不会 收到通知,但如果您使用 ThreadPoolExecutor#execute
然后 UncaughtExceptionHandler
将收到通知,因为 ThreadPoolExecutor#execute
没有将您的任务包装在 Future
中。
我正在使用 ScheduledExecutorService,它使用 ThreadFactory 创建新线程。但是当定时任务出现异常时,threadfactory中的UncaughtExceptionHandler不会被执行。为什么会这样?
线程工厂如下:
public class CustomThreadFactory {
public static ThreadFactory defaultFactory(String namePrefix) {
return new ThreadFactoryBuilder()
.setNameFormat(String.format("%s-%%d", namePrefix))
.setDaemon(false)
.setUncaughtExceptionHandler(new CustomExceptionHandler())
.build();
}
}
异常处理程序:
public class CustomExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread thread, Throwable t) {
log.error("Received uncaught exception {}", thread.getName(), t);
}
}
主要功能:
public static void main(String[] args) {
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(
CustomThreadFactory.defaultFactory("scheduler"));
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 20L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1));
scheduler.scheduleWithFixedDelay(
() -> {
executor.submit(() -> Thread.sleep(10000));
},0,1,TimeUnit.SECONDS);
}
我正在陈述我的确切问题,这样它就不会成为 XY 问题。在上面的代码片段中,阻塞队列的大小为 1,并且每秒都会将任务添加到阻塞队列中。所以在添加第三个任务时,阻塞队列执行器给出 RejectionExecutionException
,它不是由 UncaughtExceptionHandler
.
当线程由于未捕获的异常而即将终止时,Java 虚拟机将使用 Thread.getUncaughtExceptionHandler() 查询线程的 UncaughtExceptionHandler
并将调用处理程序的 uncaughtException方法,将线程和异常作为参数传递。
UncaughtExceptionHandler 将在提交的任务抛出未捕获的异常但 RejectionExecutionException
没有被任务本身抛出时调用。但是,您可以将 RejectedExecutionHandler
传递给 ThreadPoolExecutor
。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
ThreadPoolExecutor 将 RejectedExecutionHandler
作为参数。
编辑:
在你的情况下 UncaughtExceptionHandler
不是 called/notified 因为当你调用 scheduleWithFixedDelay
时不会直接执行你的 Runnable 但是它会在 ScheduledFutureTask
中扭曲它。现在,当 executor.submit(() -> Thread.sleep(10000));
抛出异常(在您的情况下为 RejectionExecutionException )时,它不会保持未捕获状态,因为 FutureTask#run
捕获了异常并将异常设置为 Future result/outcome。看FutureTask#run
的部分:
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
和setException()
方法将异常对象设置为FutureTask
的outcome
。
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
现在的问题是我们能否以某种方式获得异常的 exception/notification?
是的,这是可能的。当您在 Future 上调用 Future#get
时,将抛出异常,但 Exception
将被包裹在 ExecutionException
中。看看 Future#get
方法。
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
@SuppressWarnings("unchecked")
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
我已经在 ScheduledThreadPoolExecutor
上看到了所有可用的方法(提交、执行、计划等),将 task
包装在未来的任务中。所以我认为没有办法通过 UncaughtExceptionHandler
以防万一。
但是对于 ThreadPoolExecutor,如果您使用 ThreadPoolExecutor#submit
提交任务,您的 UncaughtExceptionHandler
将 不会 收到通知,但如果您使用 ThreadPoolExecutor#execute
然后 UncaughtExceptionHandler
将收到通知,因为 ThreadPoolExecutor#execute
没有将您的任务包装在 Future
中。