java - 当线程中发生异常时,SchduledExecutorService 不执行 UncaughtExceptionHandler

标签 java scheduler scheduledexecutorservice uncaughtexceptionhandler

我正在使用 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不会被任务本身抛出。不过,您可以通过RejectedExecutionHandlerThreadPoolExecutor .

    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()方法将异常对象设置为 outcomeFutureTask

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/

相关文章:

java - 使用 ScheduledExecutorService 在多个线程上执行 Runnable lambda

java - ScheduledExecutorService 中终止的任务会发生什么情况?

java - 使用java将数据存入数据库时​​出现异常如何跳过?

database - 使用参数创建 dbms_scheduler.create_job

Java - 使用执行器服务的线程

python - Twitter抓取重复执行代码(python)

c - 有什么比用于彩票调度程序的 LCG 更好的(伪)随机数生成器?

java - 有条件地将maven中的部分资源排除在war之外

java - 无法解决 DispatcherServlet 的 ClassNotFoundException(在 Spring 上)

java - Android - 光标结果搜索字符串错误,需要搜索 DISPLAY_NAME 进行匹配