关于java:奇怪的异常传播到主线程

Curious exception propagation to main thread

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    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");
        }
    }

我希望只有第一个被传播,因为第二个被吞下这个设置(由于它通过循环遍历数组列表顺序等待第一个runnable)。

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

为什么runnable2的例外根本打印?

我还要提一下,当在每个线程上调用stop()时,在抛出异常以允许仍然调用期货循环之前,方法内部会有一个暂停。


如果你只在第二个上调用stop(),那么for循环将永远等待第一个完成。这个例外没有被吞下;如果您的程序在第二个未来调用get(),它已被捕获并将被抛出,但您的程序已经等待第一个未来并且无法达到该点。


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

这就是Future类的本质。它包装了你的工作,所以不管正在执行和完成的作业的顺序如何,当你调用future.get()时,它将返回结果或抛出异常。

因此,如果第二个作业首先抛出异常,它将存储在与作业关联的Future中,以便在稍后通过futures列表时返回,即使第一个future.get()可能需要等待第一份工作完成

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

1
2
3
4
5
6
7
8
9
10
11
12
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);
            }

然后:

1
2
3
4
5
6
7
8
9
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中,state可以是NORMALEXCEPTIONAL