【问题标题】:Program never ends on TimeoutException in java程序永远不会在 java 中的 TimeoutException 上结束
【发布时间】:2021-03-19 03:11:57
【问题描述】:

我正在尝试一个 java 程序来理解 Future 的工作原理。

我编写了以下程序,但它永远不会结束。如果我在 Thread.sleep() 中输入一个小于 10 的值,那么它可以工作,但不适用于值 >=10

我了解导致问题的部分可能是 future.get 调用。

但是,经过进一步分析,我尝试的是处理所有异常,而不是让 jvm 处理它们。

例如:

现在它终止了。

我做了进一步的检查,发现如果我抛出 ExecutionExceptionInterruptedException 并处理 TimeoutException 它再次正常工作。 这里奇怪的部分是我必须强制处理 TimeoutException,否则它将不起作用。我不太清楚为什么这种奇怪的行为会持续存在。

我使用的是 OpenJDK 15。

如果有人想尝试代码 sn-p 在这里:

import java.util.concurrent.*;

public class FixedThreadPoolExecutorDemo2 {

    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        workWithFutureCallable(executorService);
        executorService.shutdownNow();
    }

    private static void workWithFutureCallable(ExecutorService executorService) throws ExecutionException, InterruptedException, TimeoutException {

        Future<Integer> myOtherFuture = executorService.submit(() -> {
            try {
                Thread.sleep(109);
            } catch (InterruptedException e) {
            }
            return 1000;
        });
        System.out.println("myOtherFuture  should be cancelled if running for more than specified time. ->" + myOtherFuture.get(10, TimeUnit.MILLISECONDS));

    }
}

【问题讨论】:

  • 当异常被传递时,执行器服务没有被关闭并且它正在使用一个非守护线程。 newFixedThreadPool 的文档:“池中的线程将一直存在,直到它被显式关闭”。使用(创建)一个 ThreadFactory 创建守护进程 Threads 或(更好的 IMO)使用 try-finally 始终关闭
  • ` executorService.shutdownNow();` 会在代码中调用。
  • 你必须能够在 main 方法中捕捉到线程中断并且应该退出应用程序。
  • 另外,我看到它进入线程组的 uncaughtException 方法,如果没有看到 instanceof ThreadDeath,它会打印堆栈跟踪并移动 ahed

标签: java concurrency future


【解决方案1】:

TimeoutException 没有被main 捕获,所以main 也会突然终止没有调用shutdownNow。主线程已终止,但Executors创建的线程默认为非守护线程,因此虚拟机没有关闭,Executors线程继续运行。

解决方案:

    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        try {
            workWithFutureCallable(executorService);
        } finally {
            executorService.shutdownNow();
        }
    }

实现ThreadFactory 来创建守护线程并使用它来获取服务:

        ExecutorService executorService = Executors.newFixedThreadPool(2, r -> {
            var thread = new Thread(r);
            thread.setDaemon(true);
            return thread;
        });

更好的是,至少在生产代码中,捕获并处理Exceptions(连同finally 块。)

【讨论】:

  • 嗨@user15244370,对不起,我无法通过您的 cmets 理解同样的事情。我添加了 finally 块并且它起作用了。确实,我不认为executorservice创建的线程是非守护线程。非常感谢。另外,在这里我正在研究并发性,所以是的,生产级代码将处理并且还会注意 executorservice 的关闭应该只在 finally 块中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-17
  • 2013-04-09
  • 2010-12-27
  • 1970-01-01
相关资源
最近更新 更多