使用newVirtualThreadPerTaskExecutor时程序提前退出问题排查
问题分析与解决方案
问题原因
程序直接退出的核心原因是:主线程提交完任务后立即执行完毕,JVM终止了所有虚拟线程。
Executors.newVirtualThreadPerTaskExecutor() 创建的虚拟线程池,其内部的虚拟线程默认以守护线程模式运行。当主线程(main方法)执行完所有代码后,JVM会检查是否还有非守护线程在运行——此时仅剩的虚拟线程都是守护线程,JVM会直接终止所有守护线程,导致程序提前退出,用户还没来得及输入内容。
另外,第二个线程的逻辑存在效率问题:通过isEmpty()判断后调用poll()的方式属于空轮询,会持续占用CPU资源,且isEmpty()和poll()的组合并非原子操作,存在线程安全隐患。
修复方案
方案1:让主线程持续阻塞,避免JVM退出
在main方法末尾添加阻塞逻辑,让主线程一直处于等待状态,保证虚拟线程能持续运行:
public static void main(String[] args) { // 提交两个任务的代码不变... // 让主线程无限阻塞,防止JVM退出 try { Thread.currentThread().join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }
方案2:优化消费线程的逻辑(推荐)
将消费线程中的isEmpty()+poll()替换为LinkedBlockingQueue的take()方法,该方法会阻塞直到队列中有元素,既避免空轮询浪费CPU,又保证线程安全:
executor.submit(() -> { try { while (true) { // take()会阻塞等待,直到队列中有元素 String name = names.take(); System.out.println("\n**** The name is " + name + " ****\n"); } } catch (Exception e) { e.printStackTrace(); } });
完整修复后的代码
import java.util.Scanner; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; public class TestThreads { private static final Scanner scanner = new Scanner(System.in); private static ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); private static LinkedBlockingQueue<String> names = new LinkedBlockingQueue<>(); public static void main(String[] args) { executor.submit(() -> { try { while (true) { String name = getName(); names.add(name); } } catch (Exception e) { e.printStackTrace(); } }); executor.submit(() -> { try { while (true) { String name = names.take(); System.out.println("\n**** The name is " + name + " ****\n"); } } catch (Exception e) { e.printStackTrace(); } }); // 阻塞主线程,防止JVM提前退出 try { Thread.currentThread().join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } private static String getName() { System.out.print("Enter a name: "); return scanner.nextLine(); } }
内容的提问来源于stack exchange,提问作者parsecer
相关产品推荐
相关产品推荐

