【JAVA并发编程专题】Fork/Join框架的理解和使用

简介: 【JAVA并发编程专题】Fork/Join框架的理解和使用

正文


一、Fork/Join简介


简单的说,Fork/Join是一个并行任务执行框架,能够把一个大的任务拆分成若干个小任务,并行地进行执行,最终还可以汇总各个小任务的执行结果。比如我们想计算1+2+…+100的结果,我们可以把这个大的任务拆分为10个小的任务,这10个小任务分别是1+…+10、11+…+20、…91+…+100,然后最终把这10个小任务的结果再加起来得到大任务的结果。


工作窃取算法,是指大任务被分成多个小任务的时候,这些小任务会被分到不通的任务队列中,每个任务队列会有一个工作线程来执行任务。但是在有些时候,有的线程会执行的比较快,提前完成了所有任务的执行,那么它就会去别的线程的任务队列中窃取任务进行执行,从而加快整体任务的完成。


但是窃取线程如何保证不和被窃取任务的线程冲突呢?这里用到了双端队列,即任务队列都是这种数据结构,队列绑定的工作线程都从队列头部取任务进行执行,而窃取线程会从别的队列尾部获取任务进行执行。


工作窃取算法充分利用多线程进行并行计算,提高了执行效率,同时使用双端队列减少了线程间的冲突竞争;然后,不能完全避免冲突,比如某个任务队列中仅有一个任务的时候,两个线程同时竞争。还有,该算法会创建多个线程和多个双端队列,对系统资源的消耗会增加。


二、Fork/Join使用


在使用之前,我们需要记住两个概念:


ForkJoinTask,我们需要自己定义一个ForkJoinTask,用来定义我们需要执行的任务,它可以继承RecursiveAction或者RecursiveTask,从而获取fork和join操作的能力,前者表示不需要返回值,后者则是需要返回值,比如我们上面的1+2+…+100的累加操作则是需要返回值的,所以应该继承RecursiveTask

ForkJoinPool,类似于线程池,连接池的概念,ForkJoinTask都需要交给ForkJoinPool才能执行。

@Slf4j
public class CountTask extends RecursiveTask<Integer> {
    // 设置最小子任务的阈值
    private static final int taskLimit = 10;
    private int start;
    private int end;
    public CountTask(int start,int end){
        this.start = start;
        this.end = end;
    }
    @Override
    protected Integer compute() {
        int sumResult = 0;
        boolean taskFlag = (end - start) <= taskLimit;
        if(taskFlag){
            log.info("进行计算,当前start为{},end为{}",start,end);
            // 子任务足够小了,可以开始执行
            for (int i=start;i<=end;i++){
                sumResult += i;
            }
        } else {
            log.info("需要拆分:当前start为{},end为{}",start,end);
            // 子任务还不是足够小,需要进一步分割
            int middle = (start + end)/2;
            CountTask leftTask = new CountTask(start,middle);
            CountTask rightTask = new CountTask(middle+1, end);
            // 分配任务
            leftTask.fork();
            rightTask.fork();
            // 等待子任务执行完成,进行结果的合并
            int leftResult = leftTask.join();
            int rightResult = rightTask.join();
            sumResult = leftResult + rightResult;
        }
        log.info("当前计算结果为:{}",sumResult);
        return sumResult;
    }
}

我们在compute中定义任务的拆分粒度和最小任务的执行逻辑,并通过fork和join能力来实现多线程并发。当子任务调用fork的时候,会继续执行子任务的compute;当子任务调用join的时候,会等待其所有子孙任务的执行结果。

public static void main(String[] args) throws ExecutionException, InterruptedException {
    ForkJoinPool forkJoinPool = new ForkJoinPool();
    // 计算从1累加到100,应该获取5050
    CountTask myTask = new CountTask(1,100);
    ForkJoinTask<Integer> result = forkJoinPool.submit(myTask);
    if(myTask.isCompletedAbnormally()){
        log.warn("发生了异常,{}", myTask.getException());
    }
    log.info("1+2+...+100={}",result.get());
}


相关文章
|
8天前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
|
9天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
6天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
8天前
|
存储 缓存 安全
在 Java 编程中,创建临时文件用于存储临时数据或进行临时操作非常常见
在 Java 编程中,创建临时文件用于存储临时数据或进行临时操作非常常见。本文介绍了使用 `File.createTempFile` 方法和自定义创建临时文件的两种方式,详细探讨了它们的使用场景和注意事项,包括数据缓存、文件上传下载和日志记录等。强调了清理临时文件、确保文件名唯一性和合理设置文件权限的重要性。
22 2
|
9天前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。
|
6月前
|
Java Unix 程序员
java 8 新特性讲解Optional类--Fork/Join 框架--新时间日期API--以及接口的新特性和注解
java 8 新特性讲解Optional类--Fork/Join 框架--新时间日期API--以及接口的新特性和注解
93 1
|
6月前
|
并行计算 算法 Java
探索Java并发编程:Fork/Join框架的深度解析
【5月更文挑战第29天】在多核处理器普及的时代,有效利用并发编程以提升程序性能已经成为开发者必须面对的挑战。Java语言提供的Fork/Join框架是一个强大的工具,它旨在利用多线程执行分而治之的任务。本文将通过深入分析Fork/Join框架的工作原理、关键特性以及与传统线程池技术的差异,帮助开发者更好地掌握这一高效处理并发任务的技术手段。
|
4月前
|
并行计算 算法 Java
Java面试题:解释Java中的无锁编程的概念,Java中的Fork/Join框架的作用和使用场景,Java中的CompletableFuture的作用和使用场景
Java面试题:解释Java中的无锁编程的概念,Java中的Fork/Join框架的作用和使用场景,Java中的CompletableFuture的作用和使用场景
34 0
|
6月前
|
Java 开发者
探索Java并发编程:Fork/Join框架的深度解析
【5月更文挑战第25天】在多核处理器日益普及的今天,并发编程成为了提升应用性能的关键。Java语言提供了多种并发工具,其中Fork/Join框架是一个高效且强大的工具,用于处理分而治之的任务。本文将深入探讨Fork/Join框架的原理、使用及其在实际应用中的优化策略,旨在帮助开发者更好地利用这一框架以解决复杂的并发问题。
|
6月前
|
并行计算 安全 算法
探索Java并发编程:Fork/Join框架的应用与优化
在多核处理器普及的今天,并发编程已经成为提高程序性能的重要手段。Java提供了多种并发工具,其中Fork/Join框架是处理分治任务的强大工具。本文将深入探讨Fork/Join框架的核心原理、使用场景以及性能优化技巧,帮助开发者更好地利用这一框架解决实际问题。通过实例分析,我们将看到如何有效地使用Fork/Join框架来加速计算密集型任务,并提供一系列最佳实践,以确保高效和线程安全的并发执行。