并发编程juc包学习7-ForkJoinPool分解任务

文章参考: https://blog.csdn.net/hanchao5272/article/details/79982095
这个写的很详细.

并发编程juc包学习7-ForkJoinPool分解任务

什么是ForkJoin

ForkJoin并发框架:Fork=分解 + Join=合并

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

例如:计算1+2+…1000000000,可以将其分割(Fork)为100000个小任务,每个任务计算10000个数据的相加,最终汇总(Join)这100000个小任务的计算结果进行合并,得到计算结果。

ForkJoin的主要类

ForkJoin并发框架的主要类包括:

ForkJoinPool:ForkJoin线程池,实现了ExecutorService接口和工作窃取算法,用于线程调度与管理。
ForkJoinTask:ForkJoin任务,提供了fork()方法和join()方法。通常不直接使用,而是使用以下子类:
----------RecursiveAction:无返回值的任务,通常用于只fork不join的情形。递归划分子任务,分别执行,但是并不需要合并各自的执行结果。
----------RecursiveTask:有返回值的任务,通常用于fork+join的情形。递归划分子任务,分别执行,然后递归合并计算结果。
在这里插入图片描述
在这里插入图片描述

实例代码(无返回结果)

模拟将10-11万的数据进行迁移到另一个地方去,每次最多只能迁移5000条,如何保证数据的准确?
要分割一个任务,首先要明确如何分隔,定义分隔规则.
继承extends RecursiveAction类,并重写compute方法,定义分隔个规则(最多5000天一组),当超过5000条就递归将条数分为两个任务.
例如:20000条数据,先分成10000+10000,然后在分成5000+5000+5000+5000一共4个任务.然后满足条件后在进行发送.

/**
 * ForkJoinPool中添加的是可分割的任务,任务如何可分割?
 */
public class TestRecursiveAction {

    //定义一个数据接收方,无界队列
    static Queue<String> queue = new ConcurrentLinkedQueue<>();

    //=====================内部类开始=============================
    /**
     * 需要继承ForkJoinTask的无返回值的子类,定义如何分隔任务
     */
    static class MyRecursiveAction extends RecursiveAction {

        //定义一个临界值,作为区分的界限
        private static final int THRESHOLD = 500;
        //定义开始值
        private int start;
        //定义结束值
        private int end;
        //定义需要分隔的数据
        private List<String> list;

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

        @Override//定义任务如何分隔
        protected void compute() {
            //在这里定义任务拆分规则
            if ((end - start) < THRESHOLD) {
                sendData(this.list);
            } else {
                //这里是每次发送的数据超过了临界值,要分成两个小任务
                int middle = (start + end) / 2;
                //使用递归的方式,创建左右两个任务
                MyRecursiveAction left = new MyRecursiveAction(start, middle, list);
                MyRecursiveAction right = new MyRecursiveAction(middle, end, list);
                //两个任务都开启
                left.fork();
                right.fork();
            }

        }

        //主要的发送数据的方法,这里会在每次符合条件的时候才会调用
        private void sendData(List<String> list) {
            for (int i = start; i < end; i++) {
                //模拟数据发到db2
                queue.add(list.get(i));
            }
            //每次发送延时50ms
            try {
                TimeUnit.MILLISECONDS.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    //=======================内部类结束=============================

    public static void main(String[] args) throws InterruptedException {
        //这里模拟查询出了10-11万条的数据
        List<String> list = getDate();
        int count = list.size();
        System.out.println("============从db1中取出的数据大小" + count);
        System.out.println("============该开始时db2中的数据大小" + queue.size());
        System.out.println("=======开始数据迁移========");
        //创建一个线程池
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        //添加一个可分解任务
        forkJoinPool.submit(new MyRecursiveAction(0, count, list));
        //阻塞,知道任务完成
        forkJoinPool.awaitTermination(10, TimeUnit.SECONDS);
        //ForkJoinPool也是一个线程池,也需要关闭
        forkJoinPool.shutdown();
        System.out.println("=======结束数据迁移========");
        System.out.println("================结束后db2中数据"+queue.size());
        //查询其中一个
        System.out.println(queue.peek());
    }

    //模拟数据查询
    static List<String> getDate() {
        //这里不能使用LinkedList,要ArrayList并且尽量指定大小,避免频繁扩容
        List<String> list = new ArrayList<>(111000);
        Random random = new Random();
        //查询一次10万--11万数据
        int count = (int) (random.nextDouble() * 10000) + 100000;
        //将数据写入list
        for (int i = 0; i < count; i++) {
            list.add("id=" + random.nextInt(200000));
        }
        //查询耗时10ms
        try {
            TimeUnit.MILLISECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return list;
    }
}

实例代码(有返回结果)

模拟多任务计算0-2000000000的值

/**
 * 测试有返回值的RecursiveTask有返回值的情况.
 * 求2000000000以内的加法
 */
public class TestRecursiveTask {

	//继承RecursiveTask任务,定义有返回值的可分解任务
    static class MyRecursiveTask extends RecursiveTask<Long> {
        private static final int THRESHOLD = 5000;
        private int start;
        private int end;

        public MyRecursiveTask(int start, int end) {
            this.start = start;
            this.end = end;
        }

        public MyRecursiveTask() {
        }

        @Override
        protected Long compute() {
            if ((end - start) < THRESHOLD) {
                return sumAdd(start,end);
            } else {
                int middle = (start + end) / 2;
                //创建两个子任务
                MyRecursiveTask left = new MyRecursiveTask(start, middle);
                MyRecursiveTask right = new MyRecursiveTask(middle, end);
                left.fork();
                right.fork();
                return left.join() + right.join();
            }
        }

        public Long sumAdd(int s,int e) {
            long sum = 0L;
            for (int i = s; i < e; i++) {
                sum = sum + i;
            }

            return sum;
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //使用单线程计算
        long pre = System.currentTimeMillis();
        MyRecursiveTask single = new MyRecursiveTask();
        Long aLong = single.sumAdd(0, 2000000001);
        System.out.println(aLong);
        System.out.println("========单线程花费"+(System.currentTimeMillis()-pre));

        long pre1 = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> submit = forkJoinPool.submit(new MyRecursiveTask(0, 2000000001));
        Long join = submit.join();
        //Long join = forkJoinPool.invoke(new MyRecursiveTask(0, 2000000001));
        System.out.println(join);
        System.out.println("========多线程花费"+(System.currentTimeMillis()-pre1));
        forkJoinPool.shutdown();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值