2

我正在使用 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.

4

1 回答 1

1

当线程由于未捕获的异常而即将终止时,Java 虚拟机将UncaughtExceptionHandler使用 Thread.getUncaughtExceptionHandler() 查询线程,并调用处理程序的 uncaughtException 方法,将线程和异常作为参数传递。

RejectionExecutionException 如果提交的任务抛出未捕获的异常但任务本身没有抛出,则将调用 UncaughtExceptionHandler 。但是,您可以传递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);
}

ThreadPoolExecutorRejectedExecutionHandler 作为参数。

编辑 :

在你的情况下UncaughtExceptionHandler没有被调用/通知,因为当你调用时scheduleWithFixedDelay不会直接执行你的 Runnable 但是它会在ScheduledFutureTask. 现在,当executor.submit(() -> Thread.sleep(10000)); 抛出异常(在您的情况下为 RejectionExecutionException )时,它不会保持未被捕获,因为FutureTask#run捕获了异常并将异常设置为未来结果/结果。看部分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#submit但是,在 ThreadPoolExecutor 的情况下,如果您使用您的提交任务UncaughtExceptionHandler不会收到通知,但如果您使用ThreadPoolExecutor#executethenUncaughtExceptionHandler将收到通知,因为ThreadPoolExecutor#execute不会将您的任务包装在Future.

于 2019-10-10T08:54:44.863 回答