【发布时间】: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