【问题标题】:Curious exception propagation to main thread奇怪的异常传播到主线程
【发布时间】:2017-02-28 00:11:42
【问题描述】:

在以下情况下,为什么两个异常都传播到主线程?

(这是我配置为在调用 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() 时,方法内部会有一个暂停,然后它会抛出异常以允许仍调用期货循环。

【问题讨论】:

  • 我不明白为什么不保留第二个异常
  • 当您进入循环时,两项任务都已完成。那么,你为什么希望一个人被吞下呢?
  • @MauricePerry 因为由于循环的性质,我们正在等待 runnable1 首先完成。如果我使用此设置仅在第二个线程上调用停止,则根本不会显示任何内容。如果我对第一个线程做同样的事情,我们会看到一个异常,它开始等待第二个。
  • 但是你在这里阻止了他们。所以两者都是完整的,所以没有等待。
  • @RealSkeptic 对不起,我没有提到这个测试被配置为在每个线程中在 stop() 完成之前被调用时暂停。测试一下,你会观察到相同的行为。

标签: java multithreading exception-handling future runnable


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    在以下情况下,为什么两个异常都会传播到主线程?

    这就是Future 类的本质。它包装了您的作业,因此无论作业执行和完成的顺序如何,它都会在您调用 future.get() 时返回结果或引发异常。

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

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

    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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-08
      • 2017-08-30
      • 1970-01-01
      • 1970-01-01
      • 2013-05-12
      • 1970-01-01
      相关资源
      最近更新 更多