分而治之:Fork/Join框架(构造一个1到200 000求和的任务)

Fork一词的原始含义是吃饭用的叉子,也有分叉的意思。在linux平台中,

方法fork用来创建子进程。使得系统进程可以多一个执行分组。

而join方法这里表示等待,也就是使用fork方法后系统多了一个执行分支(线程)

所以需要等待这个执行分支执行完毕,才有可能得到最终的结果。因此join方法

就表示等待。

毫无顾忌地使用fork方法开启线程处理,严重影响性能。JDK中,给出了一个ForkJoinPool

线程池。

由于线程池的优化,提交的任务和线程数量并不一一对应的关系。在绝大多数的情况下,

一个物理线程实际上需要处理多个逻辑任务的。因此,每个线程必然需要拥有一个任务队列。

因此在实际执行过程中,可能遇到一种情况:线程A已经把自己的任务都执行完了,而线程

B还有一大堆任务等着处理。

复制代码
public class CountTask extends RecursiveTask<Long> {
    private static final int THRESHOLD = 10000;
    private long start;
    private long end;

    public CountTask(long start,long end){
        this.start = start;
        this.end = end;
    }

    protected Long compute() {
        long sum = 0;
        boolean canCompute = (end-start)<THRESHOLD;
        if(canCompute){
            for(long i=start;i<end;i++){
                sum +=i;
            }
        } else {
            long step =(start +end)/100;
            ArrayList<CountTask> subTasks = new ArrayList<CountTask>();
            long pos = start;
            for(int i=0;i<100;i++){
                long lastOne = pos+step;
                if(lastOne>end) lastOne = end;
                CountTask subTask = new CountTask(pos,lastOne);
                pos+=step+1;
                subTasks.add(subTask);
                subTask.fork();
            }
            for(CountTask t:subTasks){
                sum+= t.join();
            }
        }
        return sum;
    }


    public  static void main(String[] args) {
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        CountTask task = new CountTask(0,200000L);
        ForkJoinTask<Long> result = forkJoinPool.submit(task);
        try{
            long res = result.get();
            System.out.println("sum="+res);
        }catch (InterruptedException e){
            e.printStackTrace();
        }catch (ExecutionException e){
            e.printStackTrace();
        }

    }
}

构造一个1到200 000求和的任务。

在ForkJoinTask<Long> result = forkJoinPool.submit(task);提交任务给线程池,

线程池会返回一个携带结果的任务,通过get()方法可以得到最终结果。

如果在执行get()方法时任务没有结束,那么主线程就会在get()方法时等待。

CountTask的实现,首先CountTask继承自RecursiveTask类,

可以携带返回值,这里的返回值类型设置为long类型,

THRESHOLD设置了任务分解的规模,也就是如果需要求和的总数大于

THRESHOLD个,那么任务就需要再次分解,否则

就可以直接执行。

如果和我可以直接执行,那么直接进行求和,返回结果。

否则,就对任务再次分解。每次分解时,简单地将原有任务分解成

100个等规模的小任务,并使用fork方法提交子任务。

之后,等待所有的子任务结束,

并将结果再次求和( for(CountTask t:subTasks){

sum+= t.join();

})

相关推荐
zh_xuan13 分钟前
c++ 单例模式
开发语言·c++·单例模式
coderSong256816 分钟前
Java高级 |【实验八】springboot 使用Websocket
java·spring boot·后端·websocket
老胖闲聊38 分钟前
Python Copilot【代码辅助工具】 简介
开发语言·python·copilot
Blossom.11842 分钟前
使用Python和Scikit-Learn实现机器学习模型调优
开发语言·人工智能·python·深度学习·目标检测·机器学习·scikit-learn
Mr_Air_Boy1 小时前
SpringBoot使用dynamic配置多数据源时使用@Transactional事务在非primary的数据源上遇到的问题
java·spring boot·后端
曹勖之1 小时前
基于ROS2,撰写python脚本,根据给定的舵-桨动力学模型实现动力学更新
开发语言·python·机器人·ros2
豆沙沙包?2 小时前
2025年- H77-Lc185--45.跳跃游戏II(贪心)--Java版
java·开发语言·游戏
军训猫猫头2 小时前
96.如何使用C#实现串口发送? C#例子
开发语言·c#
年老体衰按不动键盘2 小时前
快速部署和启动Vue3项目
java·javascript·vue
咖啡啡不加糖2 小时前
Redis大key产生、排查与优化实践
java·数据库·redis·后端·缓存