我正在使用 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 问题。在上面的代码片段中,阻塞队列的大小为一,并且每秒都会将任务添加到阻塞队列中。因此,在添加第三个任务时,阻塞队列执行器会给出 RejectionExecutionException
,而 UncaughtExceptionHandler
不会打印该异常。
最佳答案
当线程由于未捕获的异常而即将终止时,Java 虚拟机将查询该线程的 UncaughtExceptionHandler
使用 Thread.getUncaughtExceptionHandler() 并将调用处理程序的 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
不会被调用/通知,因为当您调用 scheduleWithFixedDelay
时不直接执行您的 Runnable,但它会在 ScheduledFutureTask
中扭曲它。 。现在当executor.submit(() -> Thread.sleep(10000));
抛出异常(在您的情况下 RejectionExecutionException )它不会保持未捕获状态,因为 FutureTask#run
捕获异常并将异常设置为 future 结果/结果。看FutureTask#run
的部分:
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
和setException()
方法将异常对象设置为 outcome
的FutureTask
。
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
现在的问题是我们能以某种方式获得异常/异常通知吗?
是的,这是可能的。当您调用 Future#get
在 Future 上,将会抛出异常,但是 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
中。
关于java - 当线程中发生异常时,SchduledExecutorService 不执行 UncaughtExceptionHandler,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58318817/