奇怪的异常传播到主线程

Curious exception propagation to main thread

在下面的例子中,为什么两个异常都会传播到主线程?

(这是我配置为在调用 stop() 时抛出运行时异常的测试):

    List<Future> futures = new ArrayList<>();
    futures.add(executorService.submit(runnable1));
    futures.add(executorService.submit(runnable2));
    Thread.sleep(1000L); // wait for runnables to run for a while
    runnable2.stop();
    runnable1.stop();

    for (Future future : futures) {
        try {
            future.get();
        } catch(Exception e) {
            System.out.println("Exception occurred");
        }
    }

我希望只有第一个被传播,因为第二个被这个设置吞没了(因为它通过按顺序遍历数组列表等待第一个可运行的)。

如果我们只调用 runnable2.stop() 就可以看到这种吞咽的例子——在这种情况下根本没有任何显示。

为什么会打印 runnable2 的异常?

我还应该提到,当在每个线程上调用 stop() 时,方法内部会在抛出异常之前暂停,以允许仍调用 futures 循环。

如果您只在第二个上调用 stop(),那么 for 循环将永远等待第一个完成。异常没有被吞噬;它已被捕获,如果您的程序在第二个未来调用 get() 时将被抛出,但您的程序已挂起等待第一个未来并且不会到达那个点。

In the following case, why is it that both exceptions get propagated to the main thread?

这就是Futureclass的本质。它包装您的作业,因此无论作业执行和完成的顺序如何,它都会 return 结果或在您调用 future.get().

时抛出异常

因此,如果第二个作业首先抛出异常,它会存储在与该作业关联的 Future 中,因此当您浏览 futures 列表时可以对其进行 returned稍后,即使第一个 future.get() 可能必须等待第一个作业完成。

如果您愿意,可以查看 FutureTask 的代码。以下是一些片段:

public void run() {
  ...
            try {
                // Gray: this calls your job, storing the result
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                // this puts ex into result and sets the state to EXCEPTIONAL
                setException(ex);
            }

然后:

public V get() throws InterruptedException, ExecutionException {
   ...

    Object x = outcome;
    if (s == NORMAL)
        return (V)x;
    if (s >= CANCELLED)
        throw new CancellationException();
    throw new ExecutionException((Throwable)x);

因此,当 运行 完成 call() 方法的结果或由此产生的异常与 state 一起存储在 outcome 中,后者可以是 NORMALEXCEPTIONAL.