并发编程18-Fork,Join框架

30 篇文章 0 订阅

1.Fork、Join

1.1概述

1.1 简介

  • Fork/Join框架是用于并行执行任务的框架,一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。
  • Fork/Join Pool采用优良的设计、代码实现和硬件原子操作机制等多种思路保证其执行性能
  • 多线程的目的不仅仅是提高程序运行的性能,但是可以充分利用CPU资源

1.1.2 原理

  • 第一步分割任务。首先我们需要有一个fork类来把大任务分割成子任务,有可能子任务还是很大,所以还需要不停的分割,直到分割出的子任务足够小。

  • 第二步执行任务并合并结果。分割的子任务分别放在双端队列里,然后几个启动线程分别从双端队列里获取任务执行。子任务执行完的结果都统一放在一个队列里,启动一个线程从队列里拿数据,然后合并这些数据。

  • Fork/Join使用两个类来完成以上两件事情:

    • ForkJoinTask:我们要使用ForkJoin框架,必须首先创建一个ForkJoin任务。它提供在任务中执行fork()和join()操作的机制,通常情况下我们不需要直接继承ForkJoinTask类,而只需要继承它的子类,Fork/Join框架提供了以下两个子类:
    • RecursiveAction:用于没有返回结果的任务。
    • RecursiveTask :用于有返回结果的任务。
    • ForkJoinPool :ForkJoinTask需要通过ForkJoinPool来执行,任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任务。

1.2 示例

1.2.1

用Fork/Join框架计算1+2+3+4的结果。
使用Fork/Join框架首先要考虑到的是如何分割任务,如果我们希望每个子任务最多执行两个数的相加,那么我们设置分割的阈值是2,由于是4个数字相加,所以Fork/Join框架会把这个任务fork成两个子任务,子任务一负责计算1+2,子任务二负责计算3+4,然后再join两个子任务的结果。


import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;

public	class CountTask extends RecursiveTask {
    private static final int THRESHOLD= 2;//阈值
    private int start;
    private int end;
    public CountTask(int start,int end) {
        this.start= start;
        this.end= end;
    }

    @Override
    protected Integer compute() {
        int sum = 0;
        //如果任务足够小就计算任务
        boolean canCompute = (end-start) <=THRESHOLD;
        if(canCompute) {
            for(int i =start; i <=end; i++) {
                sum += i;
            }
        }else{
            //如果任务大于阀值,就分裂成两个子任务计算
            int middle = (start+end) / 2;
            CountTask leftTask =new CountTask(start, middle);
            CountTask rightTask =new CountTask(middle + 1,end);
            //执行子任务
            leftTask.fork();
            rightTask.fork();
            //等待子任务执行完,并得到其结果
            int leftResult=(int)leftTask.join();
            int rightResult=(int)rightTask.join();
            //合并子任务
            sum = leftResult  + rightResult;
        }
        return sum;
    }

    public static void main(String[] args) {
        ForkJoinPool forkJoinPool =new ForkJoinPool();
        //生成一个计算任务,负责计算1+2+3+4
        CountTask task =new CountTask(1, 4);
        //执行一个任务
        Future result = forkJoinPool.submit(task);
        try{
            System.out.println(result.get());
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}

  • 通过这个例子让我们再来进一步了解ForkJoinTask,ForkJoinTask与一般的任务的主要区别在于它需要实现compute方法,在这个方法里,首先需要判断任务是否足够小,如果足够小就直接执行任务。如果不足够小,就必须分割成两个子任务,每个子任务在调用fork方法时,又会进入compute方法,看看当前子任务是否需要继续分割成孙任务,如果不需要继续分割,则执行当前子任务并返回结果。使用join方法会等待子任务执行完并得到其结果。

1.2.2

  • ForkJoinThread

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;

public class ForkJoinThread extends RecursiveTask<Integer> {

    private int begin;
    private int end;

    public ForkJoinThread(int begin, int end) {
        this.begin = begin;
        this.end = end;
    }

    private static int i=0;

    @Override
    protected Integer compute() {
        System.out.println(Thread.currentThread().getName() + " ... "+i);
        i++;
        int sum = 0;
        // 拆分任务,差值小于等于2的直接计算(如:1~3)
        if (end - begin <= 2) {
            // 计算
            for (int i = begin; i <= end; i++) {
                sum += i;
            }
        } else {
            // 拆分
            //如果 1~50=拆分=>1~25[1,(1+50)/2=25]和26~50[26~50 ]
            //如果1~49=拆分==>1~25[1,(1+49)/2=25]和26~49
            ForkJoinThread forkJoinThread1 = new ForkJoinThread(begin, (begin + end) / 2);//会自动调用ForkJoinThread的Compute方法
            ForkJoinThread forkJoinThread2 = new ForkJoinThread((begin + end) / 2 + 1, end);

            // 执行任务
            forkJoinThread1.fork();
            forkJoinThread2.fork();

            Integer a = forkJoinThread1.join();
            Integer b = forkJoinThread2.join();

            sum = a + b;
        }

        return sum;
    }


    /**
     * ForkJoinPool-1-worker-1 ... 0
     ForkJoinPool-1-worker-1 ... 1
     ForkJoinPool-1-worker-1 ... 2
     ForkJoinPool-1-worker-1 ... 3
     ForkJoinPool-1-worker-1 ... 4
     ForkJoinPool-1-worker-1 ... 5
     ForkJoinPool-1-worker-1 ... 6
     ForkJoinPool-1-worker-1 ... 7
     ForkJoinPool-1-worker-1 ... 8
     ForkJoinPool-1-worker-1 ... 9
     ForkJoinPool-1-worker-1 ... 10
     ForkJoinPool-1-worker-1 ... 11
     ForkJoinPool-1-worker-1 ... 12
     ForkJoinPool-1-worker-1 ... 13
     ForkJoinPool-1-worker-1 ... 14
     ForkJoinPool-1-worker-1 ... 15
     ForkJoinPool-1-worker-1 ... 16
     ForkJoinPool-1-worker-1 ... 17
     ForkJoinPool-1-worker-1 ... 18
     ForkJoinPool-1-worker-1 ... 19
     ForkJoinPool-1-worker-3 ... 19
     ForkJoinPool-1-worker-2 ... 20
     ForkJoinPool-1-worker-3 ... 21
     ForkJoinPool-1-worker-2 ... 22
     ForkJoinPool-1-worker-3 ... 23
     ForkJoinPool-1-worker-2 ... 24
     ForkJoinPool-1-worker-3 ... 25
     ForkJoinPool-1-worker-2 ... 26
     ForkJoinPool-1-worker-3 ... 27
     ForkJoinPool-1-worker-2 ... 28
     ForkJoinPool-1-worker-3 ... 29
     ForkJoinPool-1-worker-0 ... 30
     ForkJoinPool-1-worker-2 ... 30
     ForkJoinPool-1-worker-0 ... 32
     ForkJoinPool-1-worker-3 ... 31
     ForkJoinPool-1-worker-2 ... 33
     ForkJoinPool-1-worker-3 ... 35
     ForkJoinPool-1-worker-2 ... 36
     ForkJoinPool-1-worker-0 ... 36
     ForkJoinPool-1-worker-2 ... 38
     ForkJoinPool-1-worker-3 ... 37
     ForkJoinPool-1-worker-2 ... 40
     ForkJoinPool-1-worker-0 ... 39
     ForkJoinPool-1-worker-2 ... 42
     ForkJoinPool-1-worker-3 ... 41
     ForkJoinPool-1-worker-2 ... 44
     ForkJoinPool-1-worker-0 ... 43
     ForkJoinPool-1-worker-2 ... 46
     ForkJoinPool-1-worker-3 ... 45
     ForkJoinPool-1-worker-2 ... 48
     ForkJoinPool-1-worker-0 ... 47
     ForkJoinPool-1-worker-2 ... 50
     ForkJoinPool-1-worker-3 ... 49
     ForkJoinPool-1-worker-0 ... 52
     ForkJoinPool-1-worker-2 ... 52
     ForkJoinPool-1-worker-0 ... 54
     ForkJoinPool-1-worker-3 ... 53
     ForkJoinPool-1-worker-0 ... 56
     ForkJoinPool-1-worker-2 ... 55
     ForkJoinPool-1-worker-0 ... 58
     ForkJoinPool-1-worker-1 ... 58
     ForkJoinPool-1-worker-3 ... 57
     ForkJoinPool-1-worker-1 ... 61
     ForkJoinPool-1-worker-0 ... 60
     ForkJoinPool-1-worker-1 ... 63
     ForkJoinPool-1-worker-3 ... 62
     ForkJoinPool-1-worker-0 ... 64
     ForkJoinPool-1-worker-3 ... 66
     ForkJoinPool-1-worker-0 ... 67
     ForkJoinPool-1-worker-3 ... 68
     ForkJoinPool-1-worker-0 ... 69
     计算的值为:5050
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        ForkJoinPool pool = new ForkJoinPool(3);    //启用3个线程.
        Future<Integer> future = pool.submit(new ForkJoinThread(1, 100));//会调用fork,join的compute方法
        System.out.println("计算的值为:" + future.get());
    }

}

1.3 常用API

  • RecursiveTask
  • compute()

1.4 参考文章

好—聊聊并发(八)——Fork/Join框架介绍
Fork/Join框架原理解析
Fork/Join框架基本使用
Java–8--新特性–串并行流与ForkJoin框架
JAVA并行框架学习之ForkJoin
Fork、Join

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值