一、使用场景

         大任务拆解成多个子任务,子任务还可以继续拆解成更小的子任务,最后将这些最小的子任务用多个线程并行执行,然后合并执行结果,例如,对超过1000万个元素的数组进行排序。

需求:有一个大数据量的用户List,根据其部门id,设置部门名称。

二、基本思想

ForkJoin模型利用了分治算法的思想,将大任务不断拆解,多线程执行,最后合并结果。它的本质是一个线程池。

二、工作逻辑

每一个工作线程维护一个本地的双端队列用来存放任务。线程在运行的过程中产生新的任务(通常是因为调用了 fork())时,会放入工作队列的队尾,并且工作线程在处理自己的工作队列时,从队尾取出任务来执行。当某个工作线程的本地队列为空时,它会尝试窃取一个任务,也叫工作窃取(可能是来自于刚刚提交到 pool 的任务,也可能是来自于其他工作线程的工作队列),窃取其他线程的工作队列的任务时,从队首取出,加到自己的队列中,然后执行下面两步:

  • 如果任务足够小就直接执行。
  • 否则将任务拆分成更小的子任务。

从上面的过程可以看出,Fork join并不是预先拆分所有任务,而是在执行时动态的决定拆分。

ForkJoin有三个比较重要的方法(操作)
1. fork:开启一个新线程(或是重用线程池内的空闲线程),将任务交给该线程处理。
2. join:等待子任务的处理线程处理完毕,获得返回值。
3. compute:拆解并执行任务

三、代码如下

User.java

package com.example.demo.util;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;

    private String name;

    private Integer deptId;

    private String deptName;
}

多线程实现

package com.example.demo.util;

import com.google.common.collect.Maps;
import lombok.SneakyThrows;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;

public class QTaskUser extends RecursiveTask<List<User>> {
    // 分片阈值
    public static final int threshold = 10;
    private int start;
    private int end;
    private List<User> list;
    private Map<Integer, String> deptNameMap;

    public QTaskUser(int start, int end, List<User> list, Map<Integer, String> deptNameMap) {
        this.start = start;
        this.end = end;
        this.list = list;
        this.deptNameMap = deptNameMap;
    }

    /**
     *
     */
    @Override
    protected List<User> compute() {
        // 小于阈值直接执行
        if (end - start <= threshold) {
            for (int i = start; i < end; i++) {
                User user = this.list.get(i);
                String deptName = deptNameMap.get(user.getDeptId());
                user.setDeptName(deptName);
            }
        } else {
            // 递归拆解任务
            int middle = (start + end) / 2;
            QTaskUser leftTask = new QTaskUser(start, middle, list, deptNameMap);
            QTaskUser rightTask = new QTaskUser(middle, end, list, deptNameMap);
            invokeAll(leftTask, rightTask);
            // 等待计算完成并返回计算结果。
            leftTask.join();
            rightTask.join();

        }
        return list;
    }

    @SneakyThrows
    public static void main(String[] args) {
        List<User> list = new ArrayList<>();
        Map<Integer, String> map = Maps.newHashMap();
        for (int i = 0; i < 10000009; i++) {
            User user = User.builder().id(i).deptId(i).name(i + "name").build();
            list.add(user);
            map.put(i, "deptName+" + i);
        }


        ForkJoinPool forkJoinPool = new ForkJoinPool(4);
        QTaskUser task = new QTaskUser(0, list.size(), list,map);
        ForkJoinTask<List<User>> submit = forkJoinPool.submit(task);
        List<User> list2 = submit.get();
        System.out.println(list2.size());
        System.out.println(list2);

        forkJoinPool.shutdown();
    }
}
package com.example.demo.util;

import com.google.common.collect.Lists;
import lombok.SneakyThrows;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;

public class QTask extends RecursiveTask<List<String>> {
    // 分片阈值
    public static final int threshold = 3;
    private int start;
    private int end;
    private List<Integer> list;

    public QTask(int start, int end, List<Integer> list) {
        this.start = start;
        this.end = end;
        this.list = list;
    }

    /**
     *
     */
    @Override
    protected List<String> compute() {
        List<String> result = Lists.newArrayList();
        // 小于阈值直接执行
        if (end - start <= threshold) {
            for (int i = start; i < end; i++) {
                result.add(i + "p");
            }
        } else {
            // 递归拆解任务
            int middle = (start + end) / 2;
            QTask leftTask = new QTask(start, middle, list);
            QTask rightTask = new QTask(middle, end, list);
            invokeAll(leftTask, rightTask);
            // 等待计算完成并返回计算结果。
            List<String> list = leftTask.join();
            List<String> list1 = rightTask.join();
            result.addAll(list);
            result.addAll(list1);
        }
        return result;
    }

    @SneakyThrows
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            list.add(i);
        }
        ForkJoinPool forkJoinPool = new ForkJoinPool(4);
        QTask task = new QTask(0, list.size(), list);
        ForkJoinTask<List<String>> submit = forkJoinPool.submit(task);
        List<String> list2 = submit.get();
        System.out.println(list2.size());
        System.out.println(list2);

        forkJoinPool.shutdown();
    }
}