ForkJoinPool工作原理

分治思想

        将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。

步骤:

  1. 分解:将要解决的问题划分成若干小的同类问题
  2. 求解:当子问题划分得足够小时,用较简单的方法解决
  3. 合并:按原问题的要求,将子问题的解逐层合并构成原问题的解

归并排序

        归并排序(Merge Sort)是一种基于分治思想的排序算法。归并排序将一个大数组分成两个相等大小的子数组,对每个子数组分别进行排序,然后将两个子数组合并成一个有序的大数组。

单线程示例:

1. 对2千万数组排序

public class MergeSort {

    public static int[] mergeSort(int[] arrays, int len) {
        int mid = arrays.length / 2;
        // 终止条件
        if (mid <= len) {
            Arrays.sort(arrays);
            return arrays;
        }
        // 拆分任务
        int[] left = Arrays.copyOfRange(arrays, 0, mid);
        int[] right = Arrays.copyOfRange(arrays, mid, arrays.length);
        // 分别计算子任务
        left = mergeSort(left, len);
        right = mergeSort(right, len);
        // 合并子任务结果
        return merge(left, right);
    }

    // 合并两个有序的数组
    private static int[] merge(int[] left, int[] right) {
        int[] result = new int[left.length + right.length];
        int leftIndex = 0, rightIndex = 0, resultIndex = 0;
        while (leftIndex < left.length && rightIndex < right.length) {
            if (left[leftIndex] <= right[rightIndex]) {
                result[resultIndex++] = left[leftIndex++];
            } else {
                result[resultIndex++] = right[rightIndex++];
            }
        }
        while (leftIndex < left.length) {
            result[resultIndex++] = left[leftIndex++];
        }
        while (rightIndex < right.length) {
            result[resultIndex++] = right[rightIndex++];
        }
        return result;
    }
}

耗时结果

ForkJoinPool

原理

Fork/Join框架是JAVA7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干小任务,最终汇总每个小任务结果得到大任务结果的框架。

ForkJoinPool基于Fork/Join框架实现的线程池, 主要采用分治算法和工作窃取算法

  • 任务切分:将大的任务分割成更小粒度的小任务,让更多的线程参与执行
  • 任务窃取:通过任务窃取,充分地利用空闲线程,并减少竞争

构造函数

    public ForkJoinPool(int parallelism,
                        ForkJoinWorkerThreadFactory factory,
                        UncaughtExceptionHandler handler,
                        boolean asyncMode) {
        this(checkParallelism(parallelism),
             checkFactory(factory),
             handler,
             asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
             "ForkJoinPool-" + nextPoolId() + "-worker-");
        checkPermission();
    }
  • parallelism: 并行线程数
  • factory: 创建线程工厂
  • handler: 异常处理器
  • asycnMode: 队列工作模式. true:先进先出; false:后进先出

使用示例

继承ForkJoinTask类, 实现compute方法

递归操作提供了两个封装抽象类RecursiveAction和RecursiveTask

如果不需要返回值用RecursiveAction, 如果不需要返回值用RecursiveTask

示例1: ForkJoin多线程归并排序

@Getter
public class MergeSortTask extends RecursiveAction {

    private final int len;

    private int[] arrays;

    public MergeSortTask(int len, int[] arrays) {
        this.len = len;
        this.arrays = arrays;
    }

    @Override
    protected void compute() {
        int mid = arrays.length / 2;
        // 终止条件
        if (mid <= len) {
            Arrays.sort(arrays);
            return;
        }
        // 拆分任务
        int[] left = Arrays.copyOfRange(arrays, 0, mid);
        int[] right = Arrays.copyOfRange(arrays, mid, arrays.length);
        // 创建子任务
        MergeSortTask leftTak = new MergeSortTask(len, left);
        MergeSortTask rightTak = new MergeSortTask(len, right);
        // 多线程处理子任务
        invokeAll(leftTak, rightTak);
        // 合并子任务结果
        arrays = MergeSort.merge(leftTak.getArrays(), rightTak.getArrays());
    }
}

运行结果:

示例. ForkJoin多线程归并求和

@Getter
public class MergeSumTask extends RecursiveTask<Long> {

    private final int len;

    private int[] arrays;

    public MergeSumTask(int len, int[] arrays) {
        this.len = len;
        this.arrays = arrays;
    }

    @Override
    protected Long compute() {
        int mid = arrays.length / 2;
        // 终止条件
        if (mid <= len) {
            return MergeSum.sum(arrays);
        }
        // 拆分任务
        int[] left = Arrays.copyOfRange(arrays, 0, mid);
        int[] right = Arrays.copyOfRange(arrays, mid, arrays.length);
        // 创建子任务
        MergeSumTask leftTak = new MergeSumTask(len, left);
        MergeSumTask rightTak = new MergeSumTask(len, right);
        // 多线程处理子任务
        leftTak.fork();
        rightTak.fork();
        // 合并子任务结果
        return leftTak.join() + rightTak.join();
    }
}

执行结果: 求和操作建议直接用单线程

注意事项

  1. 避免提交大量阻塞任务, 建议阻塞任务和非阻塞任务分别使用独立的线程池
  2. 避免调用链路太深, 可能导致栈溢出, 例如用递归计算斐波那契数列会抛异常
  3. 如果是纯函数运算, 可考虑用ForkJoin
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值