【问题标题】:Simple program with Callable Future never terminates具有 Callable Future 的简单程序永远不会终止
【发布时间】:2018-03-12 17:30:03
【问题描述】:

我在玩 Callable 和 Future 时偶然发现了一个问题。

这是一段永远不会终止和超时的代码,即使 IDE 允许运行 5 秒,并且代码不需要超过 3 秒(它给出了 Time Limit Exceeded 错误):https://ideone.com/NcL0YV

/* package whatever; // don't place package name! */

import java.util.*;
import java.util.concurrent.*;
import java.lang.*;
import java.io.*;

/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        Ideone obj = new Ideone();
        Future<Integer> res = obj.doCallable();
        System.out.println(res.get());
    }
    public Future<Integer> calculate(Integer input) {
        ExecutorService executor = Executors.newFixedThreadPool(1);
        return executor.submit(() -> {
            long start = System.currentTimeMillis();
            Thread.sleep(2000);
            System.out.println("Sleep time in ms = "+(System.currentTimeMillis()-start));
            return input * input;
        });
    }
    public Future<Integer> doCallable() {
        int value = 99;
        try {
            Callable<Future> callable = () -> calculate(value);
            Future<Integer> future = callable.call();
            return future;
        } catch (final Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

这是一段类似的代码,因为我添加了“System.exit(0)”而终止(这是不可取的):https://ideone.com/HDvl7y

/* package whatever; // don't place package name! */

import java.util.*;
import java.util.concurrent.*;
import java.lang.*;
import java.io.*;

/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        Ideone obj = new Ideone();
        Future<Integer> res = obj.doCallable();
        System.out.println(res.get());
        System.exit(0);
    }
    public Future<Integer> calculate(Integer input) {
        ExecutorService executor = Executors.newFixedThreadPool(1);
        return executor.submit(() -> {
            long start = System.currentTimeMillis();
            Thread.sleep(2000);
            System.out.println("Sleep time in ms = "+(System.currentTimeMillis()-start));
            return input * input;
        });
    }
    public Future<Integer> doCallable() {
        int value = 99;
        try {
            Callable<Future> callable = () -> calculate(value);
            Future<Integer> future = callable.call();
            return future;
        } catch (final Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

请帮助我理解为什么我们需要 System.exit(0) 或 shutdown() 即使可调用任务已完成(future.get() 调用是阻塞调用)。

编辑: 由于下面的代码 sn-p,我做了以上所有操作来解决我的应用程序中线程不断增加的主要问题。我不确定如何在不涉及主线程(立即退出)的情况​​下在某个超时后自动完成这个未来。

@Override
public void publish(@NonNull final String message,
                    @NonNull final String topicArn) throws PublishingException {
    if (!publishAsync(message, topicArn)) {
        throw new PublishingException("Publish attempt failed for the message:"
                + message);
    }
}

private boolean publishAsync(final String message,
                             final String topicArn) {
    Callable<Future> publishCallable = () -> snsClient.publishAsync(topicArn, message);
    try {
        Future<PublishResult> result = publishCallable.call();
        log.debug("Asynchronously published message {} to SNS topic {}.", message, topicArn);
        return !result.isDone() || result.get().getMessageId() != null;
    } catch (final Exception e) {
        return false;
    }
}

【问题讨论】:

  • 使用这个 executor.shutdown() 方法@Ritz777
  • 您好 AKS,我编辑了问题以包含我要解决的真正问题,但我不知道如何根据您提供的建议来解决。
  • System.exit() 方法终止了整个程序,而 shutdown() 方法终止了 ThreadPool 服务。 @Ritz777
  • 一旦不再需要 ExecutorService 以释放系统资源并允许正常关闭应用程序,就应该关闭它。因为 ExecutorService 中的线程可能是非守护线程,它们可能会阻止正常的应用程序终止。换句话说,您的应用程序在完成其 main 方法后保持运行。您可以执行 system.exit(0) 调用,但最好允许您的线程完成其当前活动。 @Ritz777

标签: concurrency future callable


【解决方案1】:

void shutdown()

启动有序关闭,其中执行先前提交的任务,但不会接受新任务。如果已经关闭,则调用不会产生额外的影响。

此方法不等待先前提交的任务完成执行。使用 awaitTermination 来做到这一点。

【讨论】:

  • 我知道 shutdown() 方法,但我不确定是否每次使用执行程序服务时都需要它。为什么即使可调用任务已经完成,我们仍然需要它?
  • 是的,每次都需要,当您使用 ExecutorService 时。它与不使用 shutdown() 方法时使用的其他方式进行比较,然后您编程直到运行模式。
猜你喜欢
  • 1970-01-01
  • 2015-02-24
  • 2019-10-26
  • 2014-05-13
  • 2013-09-09
  • 2021-11-16
  • 2021-10-08
  • 2014-05-29
  • 1970-01-01
相关资源
最近更新 更多