如何正确捕获 Executor 中的 RuntimeException?

发布于 2024-08-10 11:25:36 字数 475 浏览 11 评论 0原文

假设我有以下代码:

ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(myRunnable);

现在,如果 myRunnable 抛出 RuntimeExcpetion,我怎样才能捕获它?一种方法是为 newSingleThreadExecutor() 提供我自己的 ThreadFactory 实现,并为 Thread 设置自定义 uncaughtExceptionHandler是从中出来的。另一种方法是将 myRunnable 包装到包含 try-catch 块的本地(匿名)Runnable。也许还有其他类似的解决方法。但是……不知怎的,这感觉很脏,我觉得不应该这么复杂。有干净的解决方案吗?

Say that I have the following code:

ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(myRunnable);

Now, if myRunnable throws a RuntimeExcpetion, how can I catch it? One way would be to supply my own ThreadFactory implementation to newSingleThreadExecutor() and set custom uncaughtExceptionHandlers for the Threads that come out of it. Another way would be to wrap myRunnable to a local (anonymous) Runnable that contains a try-catch -block. Maybe there are other similar workarounds too. But... somehow this feels dirty, I feel that it shouldn't be this complicated. Is there a clean solution?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

天荒地未老 2024-08-17 11:25:36

干净的解决方法是使用 ExecutorService.submit() 而不是 execute()。这将返回一个 Future,您可以使用它来检索任务的结果或异常:

ExecutorService executor = Executors.newSingleThreadExecutor();
Runnable task = new Runnable() {
  public void run() {
    throw new RuntimeException("foo");
  }
};

Future<?> future = executor.submit(task);
try {
  future.get();
} catch (ExecutionException e) {
  Exception rootException = e.getCause();
}

The clean workaround is to use ExecutorService.submit() instead of execute(). This returns you a Future which you can use to retrieve the result or exception of the task:

ExecutorService executor = Executors.newSingleThreadExecutor();
Runnable task = new Runnable() {
  public void run() {
    throw new RuntimeException("foo");
  }
};

Future<?> future = executor.submit(task);
try {
  future.get();
} catch (ExecutionException e) {
  Exception rootException = e.getCause();
}
甜扑 2024-08-17 11:25:36

在另一个可捕获运行时异常并处理它们的可运行对象中装饰可运行对象:

public class REHandler implements Runnable {
    Runnable delegate;
    public REHandler (Runnable delegate) {
        this.delegate = delegate;
    }
    public void run () {
        try {
            delegate.run ();
        } catch (RuntimeException e) {
            ... your fancy error handling here ...
        }
    }
}

executor.execute(new REHandler (myRunnable));

Decorate the runnable in another runnable which catches the runtime exceptions and handles them:

public class REHandler implements Runnable {
    Runnable delegate;
    public REHandler (Runnable delegate) {
        this.delegate = delegate;
    }
    public void run () {
        try {
            delegate.run ();
        } catch (RuntimeException e) {
            ... your fancy error handling here ...
        }
    }
}

executor.execute(new REHandler (myRunnable));
晌融 2024-08-17 11:25:36

为什么不调用 ExecutorService#submit(),获取Future 返回,然后在调用 Future#get()

Why not call ExecutorService#submit(), get the Future back and then handle possible exceptions yourself when calling Future#get() ?

夜灵血窟げ 2024-08-17 11:25:36

skaffman 是正确的,因为使用 submit 是最干净的方法。另一种方法是子类 ThreadPoolExecutor< /code>并覆盖 afterExecute(Runnable, Throwable)。如果您遵循此方法,请务必调用 execute(Runnable) 而不是 submit(Runnable)afterExecute不被调用。

根据 API 描述:

完成后调用的方法
执行给定的 Runnable。这
方法由线程调用
执行任务。如果非空,则
可抛出的是未捕获的
导致的 RuntimeExceptionError
执行突然终止。

注意:当动作包含在
任务(例如 FutureTask)
明确地或通过诸如
提交,这些任务对象捕获并
维护计算异常,以及
所以它们不会造成突然的
终止,以及内部
异常不会传递给此
方法

skaffman is correct in that using submit is the cleanest approach. An alternative approach is to subclass ThreadPoolExecutor and override afterExecute(Runnable, Throwable). If you follow this approach be sure to call execute(Runnable) rather than submit(Runnable) or afterExecute will not be invoked.

Per the API description:

Method invoked upon completion of
execution of the given Runnable. This
method is invoked by the thread that
executed the task. If non-null, the
Throwable is the uncaught
RuntimeException or Error that caused
execution to terminate abruptly.

Note: When actions are enclosed in
tasks (such as FutureTask) either
explicitly or via methods such as
submit, these task objects catch and
maintain computational exceptions, and
so they do not cause abrupt
termination, and the internal
exceptions are not passed to this
method
.

长亭外,古道边 2024-08-17 11:25:36

提交到 ThreadPoolExecutors 的任务(CallableRunnable)将被转换为 FuturnTask,包含一个名为 < b>callable 等于您提交的任务。 FuturnTask 有自己的 run 方法,如下所示。 c.call() 中抛出的所有异常或可抛出异常都将被捕获并放入名为 outcome 的 prop 中。 将会抛出outcome

FuturnTask.run From Jdk1.8 Source Code :

public void run() {
        ...
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    // save ex into `outcome` prop
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        }
        ...
    }

当调用FuturnTask的get方法时,如果你想捕获异常,

      1. skaffman's answer
      2. overwrite `afterExecute` when you new a ThreadPoolExecutor
        @Override
        protected void afterExecute(Runnable r, Throwable t) {
            super.afterExecute(r, t);
            Throwable cause = null;
            if (t == null && r instanceof Future) {
                try {
                    ((Future<?>) r).get();
                } catch (InterruptedException | ExecutionException e) {
                    cause = e;
                }
            } else if (t != null) {
                cause = t;
            }
            if (cause != null) {
                // log error
            }
        }

a task(Callable or Runnable) submitted to ThreadPoolExecutors will be convert to a FuturnTask, contains a prop named callable equals the task you submit. FuturnTask has its own run method as follows. All exception or throwable throwed in c.call() will be catched and put into a prop named outcome. When calling FuturnTask's get method, outcome will be throwed

FuturnTask.run From Jdk1.8 Source Code

public void run() {
        ...
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    // save ex into `outcome` prop
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        }
        ...
    }

if you want catch the exception :

      1. skaffman's answer
      2. overwrite `afterExecute` when you new a ThreadPoolExecutor
        @Override
        protected void afterExecute(Runnable r, Throwable t) {
            super.afterExecute(r, t);
            Throwable cause = null;
            if (t == null && r instanceof Future) {
                try {
                    ((Future<?>) r).get();
                } catch (InterruptedException | ExecutionException e) {
                    cause = e;
                }
            } else if (t != null) {
                cause = t;
            }
            if (cause != null) {
                // log error
            }
        }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文