Java ForkJoinPool线程池的使用之并行计算数组求和实例

 更新时间:2025年05月29日 09:50:49   作者:学亮编程手记  
这篇文章主要介绍了Java ForkJoinPool线程池的使用之并行计算数组求和实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

Java ForkJoinPool线程池的使用之并行计算数组求和

package com.zhangxueliang.juc;

import java.io.IOException;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.RecursiveTask;

public class ForkJoinPoolDemo {
	static int[] nums = new int[1000000];
	static final int MAX_NUM = 50000;
	static Random r = new Random();
	
	static {
		for(int i=0; i<nums.length; i++) {
			nums[i] = r.nextInt(100);
		}
		
		System.out.println("---" + Arrays.stream(nums).sum()); //stream api
	}
	

	static class AddTask extends RecursiveAction {

		int start, end;

		AddTask(int s, int e) {
			start = s;
			end = e;
		}

		@Override
		protected void compute() {

			if(end-start <= MAX_NUM) {
				long sum = 0L;
				for(int i=start; i<end; i++) sum += nums[i];
				System.out.println("from:" + start + " to:" + end + " = " + sum);
			} else {

				int middle = start + (end-start)/2;

				AddTask subTask1 = new AddTask(start, middle);
				AddTask subTask2 = new AddTask(middle, end);
				subTask1.fork();
				subTask2.fork();
			}


		}

	}

	
	static class AddTaskRet extends RecursiveTask<Long> {
		
		private static final long serialVersionUID = 1L;
		int start, end;
		
		AddTaskRet(int s, int e) {
			start = s;
			end = e;
		}

		@Override
		protected Long compute() {
			
			if(end-start <= MAX_NUM) {
				long sum = 0L;
				for(int i=start; i<end; i++) sum += nums[i];
				return sum;
			} 
			
			int middle = start + (end-start)/2;
			
			AddTaskRet subTask1 = new AddTaskRet(start, middle);
			AddTaskRet subTask2 = new AddTaskRet(middle, end);
			subTask1.fork();
			subTask2.fork();
			
			return subTask1.join() + subTask2.join();
		}
		
	}
	
	public static void main(String[] args) throws IOException {
		/*ForkJoinPool fjp = new ForkJoinPool();
		AddTask task = new AddTask(0, nums.length);
		fjp.execute(task);*/

		ForkJoinPoolDemo temp = new ForkJoinPoolDemo();

		ForkJoinPool fjp = new ForkJoinPool();
		AddTaskRet task = new AddTaskRet(0, nums.length);
		fjp.execute(task);
		long result = task.join();
		System.out.println(result);
		
		//System.in.read();
		
	}
}

ForkJoinPool 示例代码解析

这段代码演示了 Java 中 ForkJoinPool 框架的使用,展示了两种不同的任务分割方式:

  • RecursiveAction(无返回值)
  • RecursiveTask(有返回值)

代码结构分析

1. 初始化部分

static int[] nums = new int[1000000];  // 创建包含100万个元素的数组
static final int MAX_NUM = 50000;     // 任务分割的阈值
static Random r = new Random();        // 随机数生成器

// 静态初始化块:填充数组并计算总和
static {
    for(int i=0; i<nums.length; i++) {
        nums[i] = r.nextInt(100);  // 每个元素赋值为0-99的随机数
    }
    System.out.println("---" + Arrays.stream(nums).sum()); // 使用stream API计算总和作为验证基准
}

2. RecursiveAction 实现(无返回值)

static class AddTask extends RecursiveAction {
    int start, end;
    
    AddTask(int s, int e) {
        start = s;
        end = e;
    }

    @Override
    protected void compute() {
        if(end-start <= MAX_NUM) {  // 如果任务足够小,直接计算
            long sum = 0L;
            for(int i=start; i<end; i++) sum += nums[i];
            System.out.println("from:" + start + " to:" + end + " = " + sum);
        } else {  // 否则分割任务
            int middle = start + (end-start)/2;
            AddTask subTask1 = new AddTask(start, middle);
            AddTask subTask2 = new AddTask(middle, end);
            subTask1.fork();  // 异步执行子任务
            subTask2.fork();
        }
    }
}

3. RecursiveTask 实现(有返回值)

static class AddTaskRet extends RecursiveTask<Long> {
    int start, end;
    
    AddTaskRet(int s, int e) {
        start = s;
        end = e;
    }

    @Override
    protected Long compute() {
        if(end-start <= MAX_NUM) {  // 如果任务足够小,直接计算并返回结果
            long sum = 0L;
            for(int i=start; i<end; i++) sum += nums[i];
            return sum;
        } 
        
        // 分割任务
        int middle = start + (end-start)/2;
        AddTaskRet subTask1 = new AddTaskRet(start, middle);
        AddTaskRet subTask2 = new AddTaskRet(middle, end);
        subTask1.fork();  // 异步执行子任务
        subTask2.fork();
        
        return subTask1.join() + subTask2.join();  // 合并子任务结果
    }
}

4. 主方法

public static void main(String[] args) throws IOException {
    // 创建ForkJoinPool实例
    ForkJoinPool fjp = new ForkJoinPool();
    
    // 创建有返回值的任务
    AddTaskRet task = new AddTaskRet(0, nums.length);
    
    // 执行任务
    fjp.execute(task);
    
    // 获取并打印结果
    long result = task.join();
    System.out.println(result);
}

关键概念解释

ForkJoinPool:

  • Java 7引入的线程池实现
  • 使用工作窃取(work-stealing)算法提高并行效率
  • 特别适合分治(divide-and-conquer)算法

RecursiveAction:

  • 用于不返回结果的任务
  • 需要实现compute()方法
  • 示例中的AddTask只打印结果不返回

RecursiveTask:

  • 用于需要返回结果的任务
  • 需要实现compute()方法并返回指定类型
  • 示例中的AddTaskRet返回子数组的和

fork()和join():

  • fork(): 异步安排任务执行
  • join(): 等待任务完成并获取结果

执行流程

  1. 初始化一个包含100万个随机数的数组
  2. 使用Stream API计算总和作为基准
  3. 创建ForkJoinPool
  4. 创建AddTaskRet任务,范围是整个数组
  5. 任务会根据MAX_NUM阈值(50000)不断分割,直到足够小
  6. 小任务直接计算子数组和
  7. 合并所有子任务的结果得到最终总和
  8. 打印结果(应与Stream API计算的结果一致)

使用建议

  1. 对于计算密集型任务,ForkJoinPool通常比传统线程池更高效
  2. 任务分割的阈值需要合理设置,太小会导致过多任务创建开销,太大会降低并行度
  3. 有返回结果需求时使用RecursiveTask,否则使用RecursiveAction
  4. 注意join()是阻塞调用,会等待任务完成

这段代码很好地展示了ForkJoin框架的分治思想和使用方法,是并行计算数组求和的经典示例。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java实现导出数据到csv文件的示例代码

    Java实现导出数据到csv文件的示例代码

    项目中很多情况会遇到需要导出数据到csv文件,这篇文章主要为大家介绍了如何利用Java实现将数据导出到csv文件中,感兴趣的小伙伴可以了解下
    2025-05-05
  • spring mvc4的日期/数字格式化、枚举转换示例

    spring mvc4的日期/数字格式化、枚举转换示例

    本篇文章主要介绍了spring mvc4的日期/数字格式化、枚举转换示例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2017-01-01
  • Spring中字段格式化的使用小结

    Spring中字段格式化的使用小结

    Spring提供的一个core.convert包 是一个通用类型转换系统。它提供了统一的 ConversionService  API和强类型的Converter SPI,用于实现从一种类型到另一种类型的转换逻辑,这篇文章主要介绍了Spring中字段格式化的使用详解,需要的朋友可以参考下
    2022-06-06
  • Java数据类型与运算符详细代码示例

    Java数据类型与运算符详细代码示例

    变量指的是程序运行时可变的量,相当于开辟一块内存空间来保存一些数据,类型则是对变量的种类进行了划分,不同的类型的变量具有不同的特性,这篇文章主要介绍了Java数据类型与运算符的相关资料,需要的朋友可以参考下
    2026-02-02
  • java servlet结合mysql搭建java web开发环境

    java servlet结合mysql搭建java web开发环境

    之前写过一篇 servlet+oracle的文章,但是那是因为公司有可能接那么一个项目,然后我当时也比较闲,所以随便学了下,那玩意是白去研究了,因为公司后面并没接到那项目。
    2015-12-12
  • java实现直线分形山脉

    java实现直线分形山脉

    这篇文章主要为大家详细介绍了java实现直线分形山脉,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • 详解如何把Java中if-else代码重构成高质量代码

    详解如何把Java中if-else代码重构成高质量代码

    这篇文章主要介绍了详解如何把Java中if-else代码重构成高质量代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • Mybatis mapper配置文件xml存放位置

    Mybatis mapper配置文件xml存放位置

    这篇文章主要介绍了Mybatis mapper配置文件xml存放位置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2023-12-12
  • eclipse创建java项目并运行的详细教程讲解

    eclipse创建java项目并运行的详细教程讲解

    eclipse是java开发的ide工具,是大部分java开发人员的首选开发工具,可是对于一些新Java人员来说,不清楚eclipse怎么运行项目?这篇文章主要给大家介绍了关于eclipse创建java项目并运行的相关资料,需要的朋友可以参考下
    2023-04-04
  • 详解SpringBoot中自定义和配置拦截器的方法

    详解SpringBoot中自定义和配置拦截器的方法

    今天这篇文章来介绍一下拦截器在SpringBoot中的如何自定义及如何配置的,拦截器的具体作用和应用场景,感兴趣的小伙伴可以了解一下
    2022-05-05

最新评论