为何ExecutorService提交的任务无法异步执行?
问题场景
在IntelliJ中调试Java异步代码时,调用calculateAsync()方法后,提交给ExecutorService的任务并未并发执行,而是要等到main方法执行完毕才开始运行。断点设置在System.out.println("calculate async was called");处,终端输出顺序为:
inside calculate async method outside executores block calculate async was called inside the submitted task completed the thread
问题根源
每次调用calculateAsync()时,Executors.newFixedThreadPool(10)创建的线程池是局部变量,方法执行完成后该线程池失去引用,会被JVM标记为可回收对象。ThreadPoolExecutor的finalize()方法会自动调用shutdown(),虽然shutdown()不会终止已提交的任务,但在main线程运行期间,JVM的Finalizer线程可能延迟处理线程池的回收逻辑,导致任务被暂缓执行,直到main线程结束后才触发任务调度。
解决方案
方案1:将线程池改为类成员变量(推荐)
保持线程池的引用,避免被垃圾回收,同时复用线程池减少资源开销:
修改CompletableFuturePlay.java:
public class CompletableFuturePlay { // 类成员变量持有线程池引用 private final ExecutorService executor = Executors.newFixedThreadPool(10); public void calculateAsync() throws InterruptedException { System.out.println("inside calculate async method"); executor.submit(() -> { System.out.println("inside the submitted task"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("completed the thread"); }); System.out.println("outside executores block"); } // 程序结束时关闭线程池,避免资源泄漏 public void shutdown() { executor.shutdown(); } }
修改PlayGround.java,确保线程池正常关闭:
public class PlayGround { public static void main(String[] args) throws InterruptedException, ExecutionException { CompletableFuturePlay completableFuturePlay = new CompletableFuturePlay(); try { completableFuturePlay.calculateAsync(); System.out.println("calculate async was called"); // 若需观察异步任务执行,可添加短暂等待(调试时可去掉) // Thread.sleep(2000); } finally { completableFuturePlay.shutdown(); } } }
方案2:用CompletableFuture包装任务(快速验证)
使用CompletableFuture.runAsync()包装任务,它会持有任务和线程池的引用,避免提前被回收:
public void calculateAsync() throws InterruptedException { System.out.println("inside calculate async method"); CompletableFuture.runAsync(() -> { System.out.println("inside the submitted task"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("completed the thread"); }, Executors.newFixedThreadPool(10)); System.out.println("outside executores block"); }
调试验证
修改后,在System.out.println("calculate async was called");处设置断点,main线程暂停时,异步任务会立即执行,终端会提前输出inside the submitted task,符合并发预期。
内容的提问来源于stack exchange,提问作者vinter

