Jac*_*son 1 java multithreading threadpool completable-future completion-stage
这是我的简单代码:
public class Main4 {
public static void main(String[] args) {
System.out.println("Hello from thread: "+Thread.currentThread().getName());
new Game().run();
System.out.println("I am dying ... ");
}
static class Game {
public void run() {
value();
}
private int value() {
int number = 0;
CompletionStage<Void> c = calculate().thenApply(i -> i + 3).thenAccept(i -> System.out.println("I am done, and my value is " + i));
return number;
}
private CompletionStage<Integer> calculate() {
CompletionStage<Integer> completionStage = new CompletableFuture<>();
Executors.newCachedThreadPool().submit(() -> {
System.out.println("I am in the thread: " + Thread.currentThread().getName());
try {
Thread.sleep(50000);
((CompletableFuture<Integer>) completionStage).complete(3);
} catch (Exception e) {
e.printStackTrace();
}
return null;
});
return completionStage;
}
}
}
Run Code Online (Sandbox Code Playgroud)
执行的输出为:
Hello from thread: main
I am in the thread: pool-1-thread-1
I am dying ...
Run Code Online (Sandbox Code Playgroud)
但问题是:主线程不会立即终止,它会等待 50000 毫秒。这是我的问题。我知道它应该终止,因为没有更多的事情要执行。
最初我认为原因是“睡眠”正在主线程中执行,这就是为什么我打印了线程的名称,它们是两个不同的线程。
感谢帮助。
我在程序的输出中添加了时间标记,还添加了一个关闭挂钩,以便也可以记录 JVM 终止:
0s Hello from thread: main # `main` method starts
0s I am in the thread: pool-1-thread-1 # `Runnable` submitted to the executor starts
0s I am dying ... # `main` method exits
50s I am done, and my value is 6 # `Runnable` submitted to the executor finishes
110s exiting # JVM process exits
Run Code Online (Sandbox Code Playgroud)
方法退出后进程继续的原因main是JVM需要等待所有非守护线程终止才关闭。使用Executors类生成的执行器默认创建非守护线程(请参阅Executors.defaultThreadFactory()方法 javadoc)。
您可以通过将自定义ThreadFactory传递给Executors.newCachedThreadPool()方法来覆盖线程的创建方式:
ExecutorService executorService = Executors.newCachedThreadPool(runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t;
});
Run Code Online (Sandbox Code Playgroud)
executorService其线程池中只有守护线程。
thenAccept但请注意,在执行块后 60 秒内 JVM 仍然不会退出:
50s I am done, and my value is 6 # `Runnable` submitted to the executor finishes
110s exiting # JVM process exits
Run Code Online (Sandbox Code Playgroud)
这是为什么?Executors.newCachedThreadPool()文档中对此进行了解释(添加了重点):
创建一个线程池,该线程池根据需要创建新线程,但会重用以前构造的线程(当它们可用时)。这些池通常会提高执行许多短期异步任务的程序的性能。对执行的调用将重用先前构造的线程(如果可用)。如果没有可用的现有线程,则会创建一个新线程并将其添加到池中。六十秒内未使用的线程将被终止并从缓存中删除。因此,保持空闲足够长的时间的池将不会消耗任何资源。请注意,可以使用 ThreadPoolExecutor 构造函数创建具有相似属性但不同细节(例如超时参数)的池。
这意味着该线程池不会在线程完成计划任务后立即释放它们。相反,它会尽力重用以前创建的线程来提交新任务。这就是延迟的原因:任务完成后,线程仍保留在线程池中以供重用,并且仅在接下来的 60 秒后才被销毁(您在程序中只提交了一项任务)。只有这样 JVM 才能退出(因为该线程不是上面指出的守护线程)。
通常,在使用 ExecutorService 时,您应该在进程终止之前显式关闭它。为此,请使用ExecutorService.shutdown()或ExecutorService.shutdownNow()方法。请参阅文档了解两者之间的区别。
在 Java 中将 ExecutorService 转为守护进程
当所有 ExecutorService 任务完成时,程序不会立即终止
修改后的程序带有时间标记和 JVM 终止日志:
public class Main {
private static final Instant start = Instant.now();
private static void debug(String message) {
System.out.println(Duration.between(start, Instant.now()).getSeconds() + "s " + message);
}
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> debug("exiting")));
debug("Hello from thread: "+Thread.currentThread().getName());
new Game().run();
debug("I am dying ... ");
}
static class Game {
public void run() {
value();
}
private int value() {
int number = 0;
CompletionStage<Void> c = calculate().thenApply(i -> i + 3).thenAccept(i -> debug("I am done, and my value is " + i));
return number;
}
private CompletionStage<Integer> calculate() {
CompletionStage<Integer> completionStage = new CompletableFuture<>();
Executors.newCachedThreadPool().submit(() -> {
debug("I am in the thread: " + Thread.currentThread().getName());
try {
Thread.sleep(50000);
((CompletableFuture<Integer>) completionStage).complete(3);
} catch (Exception e) {
e.printStackTrace();
}
return null;
});
return completionStage;
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2320 次 |
| 最近记录: |