为什么使用ForkJoinPool提交任务会出现执行两次的情况?
代码复现
import java.util.concurrent.ForkJoinPool; public class Test { public static void main(String[] args) { ForkJoinPool forkJoinPool = new ForkJoinPool(3); forkJoinPool.submit(() -> { System.out.println("Inside..."); }).invoke(); } }
两次打印的原因
ForkJoinPool.submit()会把任务提交到池的共享调度队列,池内的工作线程会异步抢占执行该任务。- 后续你调用了submit返回的
ForkJoinTask的invoke()方法,由于调用方是普通的main线程,不属于当前ForkJoinPool的工作线程,触发了ForkJoin的外部线程协助执行逻辑:main线程会直接尝试执行这个未完成的任务,不需要等待池内线程调度。 - 此时存在竞态条件:如果submit提交任务后,池内的工作线程已经取出了任务、但还没来得及更新任务的执行状态,main线程调用invoke()时会判定任务还没执行,也会启动任务执行,最终同一个任务被池线程和main线程各执行一次,就会出现两次打印。
- 这个现象不是必现的,和CPU调度有关:如果池内线程已经执行完任务,main线程调用invoke()时只会拿结果,不会重复执行,就只会打印一次。
修复方案
要保证任务只执行一次,选任意一种改法即可:
- 提交异步任务后用
join()等待结果,不要调用invoke():
forkJoinPool.submit(() -> { System.out.println("Inside..."); }).join();
- 不需要异步提交的话,直接用
ForkJoinPool.invoke()提交任务,不用先submit:
forkJoinPool.invoke(() -> { System.out.println("Inside..."); });
内容的提问来源于stack exchange,提问作者3AKOBAH
相关产品推荐
相关产品推荐

