【JUC】十、ForkJoin

news/2024/5/20 6:51:04 标签: JUC, Java, 并发

文章目录

  • 1、分支合并框架
  • 2、案例
  • 3、ForkJoinTask
  • 4、工作窃取算法
  • 5、ForkJoinPool

一个个任务执行在一个个线程上,倘若某一个任务耗时很久,期间其他线程都无事可做,显然没有利用好多核CPU这一计算机资源,因此,出现了"分而治之"的解决方案。

在这里插入图片描述

1、分支合并框架

Fork/Join即分支合并,用处是把一个大任务拆分成多个子任务来并行处理,最后再合并子任务的结果。(since Java1.7)

  • Fork:把一个复杂的任务进行拆分,大事化小
  • Join:把拆分的子任务的结果做合并

示意图:

在这里插入图片描述

举个实际例子:
在这里插入图片描述

2、案例

以下采用分支合并框架来实现1+2+…+100

在这里插入图片描述

通过继承RecursiveTask类自定义任务,泛型为result的类型:

//自定义任务类继承RecursiveTask
class MyTask extends RecursiveTask<Integer> {

    //拆分差值不超过10,来计算10以内的运算
    private static final int VALUE = 10;
    //拆分起始值
    private int begin;
    //拆分结束值
    private int end;
    //返回结果
    private int result;

    //构造中传入起始值
    public MyTask(int begin, int end) {
        this.begin = begin;
        this.end = end;
    }

    //拆分与合并
    @Override
    protected Integer compute() {
        //判断即将要相加的两个数差值是否大于10
        if (end - begin <= VALUE) {
            for (int i = begin; i <= end; i++) {
                result = result + i;
            }
        //否,则进一步拆分
        } else {
            //中间值
            int middle = (begin+end) /2;
            //左边
            MyTask task01 = new MyTask(begin, middle);
            //右边
            MyTask task02 = new MyTask(middle + 1, end);
            //调用方法拆分
            task01.fork();
            task02.fork();
            //合并结果
            result = task01.join() + task02.join();

        }
        return result;
    }
}

以上自定义Task中体现的思路就是:问题足够小的时候,直接处理,否则就继续拆分。创建forkjoinpool对象,提交任务,获取执行结果:

public class ForkJoinDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建拆分任务对象
        MyTask myTask = new MyTask(0, 100);
        //创建分支合并池对象
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Integer> forkJoinTask = forkJoinPool.submit(myTask);
        //获取最终合并之后的结果
        Integer result = forkJoinTask.get();
        System.out.println(result);
        //关闭池对象
        forkJoinPool.shutdown();
    }

}

结果:

5050

3、ForkJoinTask

在这里插入图片描述
ForkJoinTask有两个子类:Recursive即递归的意思

  • RecursiveTask:有返回值的任务
  • RecursiveAction:没有返回值的任务

ForkJoinTask还实现了Future接口,即说明是一个可以异步获取结果的任务。常用方法有:

  • fork:分割子任务,判断当前线程是否可以转型为ForkJoinWorkerThread类型,即是不是通过ForkJoinPool线程池执行的ForkJoinTask,是就push到队列里

在这里插入图片描述

  • 合并子任务结果:join
//调用方法拆分
task01.fork();
task02.fork();
//合并结果
result = task01.join() + task02.join();
  • 获取任务执行结果:get,即等待任务执行完成并返回任务计算结果

【方法源码详解参考这篇】

4、工作窃取算法

先看类ForkJoinWorkerThread类,它是执行前面ForkJoinTask的线程。ForkJoin框架使用的线程维护了一个双端队列,并通过工作窃取算法(work-stealing)来提高任务执行的效率。

在这里插入图片描述

工作窃取算法,在ForkJoin框架中就是一个线程的任务队列里的活儿都干完了,就去其他线程维护的任务队列的尾部窃取一个任务来执行。用张网图示意:

在这里插入图片描述

因此:

  • 对于线程自身维护的任务队列,线程自身执行任务的顺序是后进先出
  • 对于窃取其他线程队列的线程,则是先进先出(从队尾窃取并执行)

如图,对于线程1,任务的出入队都在队首,而负责窃取任务的线程2,其从队尾取任务,也就是说,只有线程1的任务队列只剩最后一个任务的时候,才会抢夺一次锁。如此,就大大提高了并发任务的执行效率。

5、ForkJoinPool

FoolJoinPool是运行ForkJoinTask的线程池,同ThreadPoolExecutor一样,也实现了Executor和ExecutorService接口

在这里插入图片描述

任务提交到ForkJoinPool后,池里维护的线程来执行任务

ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Integer> forkJoinTask = forkJoinPool.submit(myTask);

其构造方法有四个参数:

public ForkJoinPool(int parallelism,
                        ForkJoinWorkerThreadFactory factory,
                        UncaughtExceptionHandler handler,
                        boolean asyncMode){
//.....

分别是:

  • parallelism:期望并发数,默认会使用Runtime.getRuntime().availableProcessors()的值,即当前计算机可用的CPU数量
  • factory:创建ForkJoin工作线程的工厂,默认为defaultForkJoinWorkerThreadFactory
  • handler:执行任务时遇到不可恢复的错误时的处理程序,默认为null
  • asyncMode:工作线程获取任务使用FIFO(先进先出)模式还是LIFO模式,默认为LIFO(后进先出)

http://www.niftyadmin.cn/n/5201111.html

相关文章

链路聚合-静态和动态区别

链路聚合之动静态聚合方式 链路聚合组是由一组相同速率、以全双工方式工作的网口组成。 1、动态聚合&#xff1a; 动态聚合对接的双方通过交互LACP(链路聚合控制协议)协议报文&#xff0c;来协商聚合对接。 优点&#xff1a;对接双方相互交互端口状态信息&#xff0c;使端口…

网工内推 | 字节原厂,正式编,网络工程师,最高30K*15薪

01 字节跳动 招聘岗位&#xff1a;网络虚拟化高级研发工程师 职责描述&#xff1a; 1、负责字节跳动虚拟网络产品的研发&#xff0c;包括但不局限于网络VPC、NAT、LB负载均衡等&#xff1b; 2、负责字节跳动网络基础平台的研发&#xff0c;包括但不局限于网络控制面系统、容器…

Spring Cloud Alibaba Sentinel 简单使用

Sentinel Sentinel 主要功能Sentinel 作用常见的流量控制算法计数器算法漏桶算法 令牌桶算法Sentinel 流量控制Sentinel 熔断Sentinel 基本使用添加依赖定义资源定义限流规则定义熔断规则如何判断熔断还是限流自定义 Sentinel 异常局部自定义异常全局自定义异常系统自定义异常…

wait和notify使用案例

/*** 开启四个线程 两个waitThread和 两个notifyThread * 使用notifyThread唤醒waitThread线程*/ public class NotifyExample {public static void main(String[] args) {final Object lock new Object();Thread waitThread1 new Thread(() -> {synchronized (lock) {Sys…

LeetCode【55】跳跃游戏

题目&#xff1a; 思路&#xff1a; https://blog.csdn.net/jiuweihu521/article/details/129612419 代码&#xff1a; class Solution {public static boolean canJump(int[] nums) { //前n-1个元素能够跳到的最远距离int k 0;for (int i 0; i < k; i) {//第i个元素…

泵类设备常见的5种故障及监测方法

在各种工业领域中&#xff0c;泵是一种关键设备&#xff0c;用于输送液体或气体。然而&#xff0c;泵类设备常常会面临各种故障&#xff0c;这可能导致生产停顿和生产效率下降。为了及时监测并解决这些故障&#xff0c;设备状态监测系统成为一种重要的工具。本文将介绍泵类设备…

Redis跳跃表

前言 跳跃表(skiplist)是一种有序数据结构&#xff0c;它通过在每一个节点中维持多个指向其他节点的指针&#xff0c;从而达到快速访问节点的目的。 跳跃表支持平均O(logN)&#xff0c;最坏O(N)&#xff0c;复杂度的节点查找&#xff0c;还可以通过顺序性来批量处理节点…

深度学习领域中的耦合与解耦

在阅读论文的时候应该会看到两个操作&#xff0c;一个是耦合&#xff0c;一个是解耦&#xff0c;经常搭配着出现的就是两个词语&#xff0c;耦合头&#xff08;Coupled head&#xff09;以及Decoupled head&#xff08;解耦合头&#xff09;&#xff0c;那为什么要耦合&#xf…