并发学习记录18:forkjoin

概念

fork/join是JDK1.7加入的新的线程池的实现,体现的是一种分治思想,适用于能够进行任务拆分的cpu密集型运算。
任务的拆分就是将一个大的任务拆分为算法上相同的小任务,直到拆分到可以直接求解,或者说是拆分到问题规模足够小以至于直接求解的效率此时要比再拆分求解的效率更高。常用于跟递归相关的一些计算,比如说归并,斐波拉契数列等等都可以用分治思想进行求解

Fork/Join在分治的基础上加入了多线程,可以把每个任务的分解和合并交给不同的线程来完成,进一步提升了运算效率

Fork/Join默认会创建与cpu核心数大小相同的线程池

使用

提交给Fork/Join线程池的任务需要继承RecursiveTask(有返回值)或RecursiveAction(没有返回值)。假如有一个对整数1-n求和的任务。

import lombok.extern.slf4j.Slf4j;

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

@Slf4j(topic = "ch.TestForkJoin01")
public class TestForkJoin01 {
    public static void main(String[] args) {
        ForkJoinPool forkJoinPool = new ForkJoinPool(4);
//        System.out.println("分治结果: " + forkJoinPool.invoke(new AddTask01(5)));
        System.out.println(forkJoinPool.invoke(new RealAddTask01(1, 5)));
    }
}

//假设是要做从1到5的累加,初级版本
@Slf4j(topic = "ch.AddTask01")
class AddTask01 extends RecursiveTask<Integer> {
    private int n;

    public AddTask01(int n) {
        this.n = n;
    }

    @Override
    public String toString() {
        return "分出来的新任务{" +
                "n=" + n +
                '}';
    }

    //相当于把整个大任务拆分成小任务的分治策略
    @Override
    protected Integer compute() {
        if (n == 1) {
            log.debug("join() {}", n);
            return 1;
        }
        //拆分成更小的任务
        AddTask01 t1 = new AddTask01(n - 1);
        //分
        t1.fork();

        log.debug("fork()  {} + {}", n, t1);
        //合并
        int result = n + t1.join();
        log.debug("join() {} + {} = {}", n, t1, result);
        return result;
    }
}

//二分法,使得任务之间的依赖更少,程序执行的效率更高
@Slf4j(topic = "ch.RealAddTask01")
class RealAddTask01 extends RecursiveTask<Integer> {
    private Integer left;
    private Integer right;

    public RealAddTask01(Integer left, Integer right) {
        this.left = left;
        this.right = right;
    }

    @Override
    protected Integer compute() {
        if (left == right) {
            return left;
        }
        int min = left;
        int max = right;
        int mid = (min + max) >> 1;
        RealAddTask01 t1 = new RealAddTask01(left, mid);
        RealAddTask01 t2 = new RealAddTask01(mid + 1, right);
        t1.fork();
        t2.fork();
        Integer result = t1.join() + t2.join();
        return result;
    }
}

第一种分治方式:
假设F(5)表示的含义是从一累加到五
那么这种分治方式就是
F(5) = 5 + F(4)
F(4) = 4 + F(3)
...
任务之间相互依赖,当被依赖的任务结果求出,自己这个任务的结果才能求出,效率不高,如下图:

第二种分治方式:
假设F(1,5)表示的含义是从一累加到五
那么分治方式就是
F(1,5) = F(1,3) + F(4,5)
F(1,3) = F(1,2) + F(3,3)
F(4,5) = F(4,4) + F(5,5)
这样分治,任务之间的依赖降低,效率会提高。

posted @   理塘DJ  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示