为什么forkjoin框架中,主线程会参与任务的执行?

之前一直认为forkjoin有自己的线程池,那么执行任务的时候应该是使用池中的线程, 但是在实际运用过程中遇到一些问题,不管是任务是否需要拆分,主线程都会执行任务;
测试demo如下:

image.png
public class UpdateTask extends RecursiveTask<Long> {

List<User> list;

static final int THRESHOLD = 2;

int start ;

int end ;

private UserService userService;

public UpdateTask(List<User> list, int start, int end , UserService userService) {
    System.out.println("初始化的线程id:"+Thread.currentThread().getName());

    this.list = list;
    this.start = start;
    this.end = end;
    this.userService = userService;
}

@Override
protected Long compute() {
    System.out.println("分配的线程id:"+Thread.currentThread().getName());
    int batchCount = end - start;
    if (batchCount<= THRESHOLD){
        for (int i = start; i < end; i++) {
            User user = list.get(i);
            int i1 = user.getAge() + 1;
            user.setAge(i1);
            userService.updateById(user);
        }
        System.out.println("执行的线程id:"+Thread.currentThread().getName());
        return 0L;
    }

    int middle = (end+start)/2;

    //递归
    UpdateTask task1 = new UpdateTask(list,start,middle,userService);
    UpdateTask task2 = new UpdateTask(list,middle,end,userService);
    invokeAll(task1,task2);

    return 0L;
}

}

运行结果如下:
主线程id:http-nio-8001-exec-2
初始化的线程id:http-nio-8001-exec-2
分配的线程id:http-nio-8001-exec-2
初始化的线程id:http-nio-8001-exec-2
初始化的线程id:http-nio-8001-exec-2
分配的线程id:http-nio-8001-exec-2
分配的线程id:ForkJoinPool.commonPool-worker-0
初始化的线程id:ForkJoinPool.commonPool-worker-0
初始化的线程id:ForkJoinPool.commonPool-worker-0
分配的线程id:ForkJoinPool.commonPool-worker-0
分配的线程id:ForkJoinPool.commonPool-worker-9
执行的线程id:http-nio-8001-exec-2
执行的线程id:ForkJoinPool.commonPool-worker-0
执行的线程id:ForkJoinPool.commonPool-worker-9

在网上找了一些资料,说法都是主线程不会参与任务的执行,故有此问

阅读 1.9k
2 个回答

通过反向追踪,定位到源码里面的这一行;

private int externalAwaitDone() {
    int s = ((this instanceof CountedCompleter) ? // try helping
             ForkJoinPool.common.externalHelpComplete(
                 (CountedCompleter<?>)this, 0) :
             ForkJoinPool.common.tryExternalUnpush(this) ? doExec() : 0);
    if (s >= 0 && (s = status) >= 0) {
        boolean interrupted = false;
        do {
            if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
                synchronized (this) {
                    if (status >= 0) {
                        try {
                            wait(0L);
                        } catch (InterruptedException ie) {
                            interrupted = true;
                        }
                    }
                    else
                        notifyAll();
                }
            }
        } while ((s = status) >= 0);
        if (interrupted)
            Thread.currentThread().interrupt();
    }
    return s;
} 

主线程 通过invoke 进入该方法, 有一定几率满足 ForkJoinPool.common.tryExternalUnpush(this) 的条件, 然后弹出的任务由主线程执行

你可以使用execute 尝试下,invoke 涉及等待,主线程不用岂不是浪费?

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题