当前位置: 首页 > article >正文

Java 入门指南:Java 并发编程 —— Fork/Join 框架 实现任务的拆分与合并

Fork/Join

Fork/Join 是Java并发编程中的一个框架,用于解决大型任务的并行执行问题。它于 Java 7中引入,旨在简化对多核处理器上可并行执行任务的开发

Fork/Join 框架基于分治(divide and conquer)的设计思想。它将大型任务划分为更小的子任务(称为fork),这些子任务可以并行执行。一旦所有子任务都完成,它们的结果将被合并(称为join)以生成最终的结果。

优势之处

  • 自动并行化:Fork/Join框架自动处理任务的并行执行,开发者只需要关注任务本身的逻辑。

  • 高效利用多核:Fork/Join框架能够根据系统的处理器核心数自动调整线程数量,以充分利用多核处理器的计算能力。

  • 易于实现复杂任务:通过递归拆分任务,可以轻松实现复杂的并行计算逻辑。

组成部分

1. ForkJoinPool

  • ForkJoinPool 是Fork/Join框架的核心组件之一,它是一个特殊的线程池,专门用于执行Fork/Join任务。与普通的线程池相比,它提供了更高级别的任务管理和调度机制。
  • ForkJoinPool 可以自动调整线程的数量,以适应当前系统可用的处理器核心数,从而最大化并行执行的效率。

2. ForkJoinTask

  • ForkJoinTask 是所有Fork/Join任务的基类,它定义了任务的执行方式和生命周期。
  • ForkJoinTask 可以通过 fork() 方法提交给ForkJoinPool,这会导致任务被异步执行,而当前线程可以继续执行其他任务。
  • 任务可以通过 join() 方法等待其完成,并获取结果。

3. RecursiveAction

  • RecursiveAction 是一个没有返回值的Fork/Join任务,通常用于执行计算密集型任务。
  • 当任务足够小或者达到了某个阈值时,它会直接执行任务;否则,它会将任务分解为更小的子任务,并递归地调用 fork() 方法提交给线程池。

4. RecursiveTask

  • RecursiveTask 是一个有返回值的Fork/Join任务,通常用于执行计算密集型任务,并返回一个计算结果。
  • 同样地,当任务足够小或者达到了某个阈值时,它会直接执行任务;否则,它会将任务分解为更小的子任务,并递归地调用 fork() 方法提交给线程池。
  • 任务的结果可以通过 compute() 方法计算,并通过 join() 方法获取。

实现步骤

Fork/Join 框架的核心类是 java.util.concurrent.ForkJoinPool 。以下是使用 Fork/Join 模式的一般步骤:

  1. 定义一个继承自 RecursiveTaskRecursiveAction 的任务类:RecursiveTask 用于产生结果,RecursiveAction 用于没有返回结果的任务。

  2. 在任务类中实现 compute() 方法:在 compute() 方法中,定义任务的逻辑,包括将任务拆分为子任务的规则和处理任务的终止条件。

  3. 在需要执行 Fork/Join 任务的地方创建 ForkJoinPool 对象:ForkJoinPool处理任务的线程池。

  4. 创建任务实例,并调用 ForkJoinPoolinvoke()submit() 方法提交任务:

    • invoke() 方法将任务提交并等待它的完成

    • submit() 方法返回一个 Future 对象,使用它来获取任务的状态和结果。

  5. 处理任务的结果:一旦任务完成,从 Future 对象中获取结果并进行必要的处理。

Fork/Join 框架适用于一些需要将大型任务划分为更小的可并行执行的子任务的情况。通过利用多核处理器的并行性,提高应用程序的性能和响应能力。

使用示例

以下是一个简单的Fork/Join框架的示例,展示了如何使用 ForkJoinPoolRecursiveTask 来计算数组中的元素之和:

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

public class ForkJoinSumCalculator extends RecursiveTask<Integer> {
    private final int threshold = 5; // 任务拆分的阈值
    private final int[] data;
    private final int start;
    private final int end;

    public ForkJoinSumCalculator(int[] data, int start, int end) {
        this.data = data;
        this.start = start;
        this.end = end;
    }

    @Override
    protected Integer compute() {
        int length = end - start;
        if (length <= threshold) {
            // 直接计算
            return computeDirectly();
        } else {
            // 拆分任务
            int middle = start + length / 2;
            ForkJoinSumCalculator leftTask = new ForkJoinSumCalculator(data, start, middle);
            ForkJoinSumCalculator rightTask = new ForkJoinSumCalculator(data, middle, end);

            // 异步执行子任务
            leftTask.fork();
            rightTask.fork();

            // 合并子任务的结果
            return leftTask.join() + rightTask.join();
        }
    }

    private int computeDirectly() {
        int sum = 0;
        for (int i = start; i < end; i++) {
            sum += data[i];
        }
        return sum;
    }

    public static void main(String[] args) {
        int[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinSumCalculator calculator = new ForkJoinSumCalculator(data, 0, data.length);
        int result = pool.invoke(calculator);
        System.out.println("数组的总和为: " + result);
    }
}

示例说明:

  1. 任务类定义ForkJoinSumCalculator 继承自 RecursiveTask<Integer>,它代表一个有返回值的Fork/Join任务。构造方法初始化了任务所需的数据范围。

  2. 任务执行方法 (compute):compute 方法定义了任务的执行逻辑。如果任务足够小(小于阈值),则直接计算并返回结果;否则,任务被拆分成两个子任务,并通过 fork() 方法提交给线程池。

  3. 直接计算方法 (computeDirectly):computeDirectly 方法实现了直接计算数据范围内元素之和的功能。

  4. 主程序 (main):创建一个包含数字的数组,并实例化 ForkJoinSumCalculator 对象。创建一个 ForkJoinPool 实例,并通过 invoke 方法启动任务。获取计算结果并输出。


http://www.kler.cn/news/293457.html

相关文章:

  • 探索PDF的奥秘:pdfrw库的神奇之旅
  • 2024,中国服务器操作系统迎云智主升浪
  • 鸿蒙开发5.0【高级图表实现】 解决方案
  • c++的类和对象
  • vue-seamless-scroll(二)点击事件
  • 为什么深度学习用GPU而不是CPU
  • electron 客户端 windows linux(麒麟V10)多系统离线打包 最新版 <二>
  • FPGA开发:Verilog基础语法
  • 如何进行不同数据库的集群操作?--从部署谈起,今天来看MySQL和NoSql数据库Redis的集群
  • FFMPEG -- 音频开发
  • 初识命名空间
  • S7-PLC
  • 安装win7鼠标键盘不能动原因分析及解决办法
  • 【AI学习】聊两句深度学习的目标函数
  • 计算机网络27——Linux1
  • 黑马JavaWeb开发笔记14——Tomcat(介绍、安装与卸载、启动与关闭)、入门程序解析(起步依赖、SpringBoot父工程、内嵌Tomcat)
  • EmguCV学习笔记 VB.Net 10.2 人脸识别 FaceRecgnizer类
  • 基于C++实现一个房贷计算小程序(含代码)
  • C++---由优先级队列认识仿函数
  • 《OpenCV计算机视觉》—— 图像形态学(腐蚀、膨胀等)
  • OpenGL GLFW OIT 实现
  • javaEE-多线程(3)
  • 亿佰特-NT1/NT1-B串口转RJ45以太网模块
  • python 实现newton raphson牛顿-拉夫森算法
  • 在Go语言中,不同类型之间转换的一些主要方法:
  • [数据集][目标检测]灭火器检测数据集VOC+YOLO格式3255张1类别
  • Java设计模式【备忘录模式】-行为型
  • 鸿蒙系统之ArkTs布局组件
  • Ansible在CentOS下批量部署Nginx到Kubernetes集群
  • 认识meson 的使用