Fork/Join 线程池

简述

  Fork/Join 是 JDK 1.7 加入的新的线程池实现,它体现的是一种分治思想,适用于能够进行任务拆分的 cpu 密集型运算Fork/Join 在分治的基础上加入了多线程,可以把每个任务的分解和合并交给不同的线程来完成,进一步提升了运算效率Fork/Join 默认会创建与 cpu 核心数大小相同的线程池

使用

  基本的使用流程就是创建任务然后交给线程池执行

  这里的任务就不是runnable和callable的对象了,得用RecursiveTaskRecursiveAction的子类,前者有返回结果,后者没有返回结果

  forkjoin线程池的分治思想和递归很类似,比如我们想要求1到n的和,若n=5时,f(5) = 5 + f(4) f(4) = 4 + f(3)以此类推可以进行一个任务的拆分

  重写的compute方法就是该递归方法

package ThreadTest.ThreadPool;

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

public class TestFork {
    public static void main(String[] args) {
        ForkJoinPool pool = new ForkJoinPool(5);
        System.out.println(pool.invoke(new MyTask(5)));
        //f(5) = 5 + f(4) f(4) = 4 + f(3)...
    }
}

class MyTask extends RecursiveTask<Integer> {

    private int n;

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

    @Override
    protected Integer compute() {
        if(n==1) return 1;//终止条件
        MyTask t1 = new MyTask(n-1);
        t1.fork();//让一个线程去执行
        Integer result = n + t1.join();//获取任务结果
        return result;
    }
}
sample

  任务拆分的优化,本来我们的任务拆分是这样的:

  可以看到线程的并行度很低,线程都要依赖下一个拆分任务的线程的结束才能返回

  我们可以改变任务拆分的方式:

class AddTask1 extends RecursiveTask<Integer> {
    int n;
    public AddTask1(int n) {
        this.n = n;
    }
    @Override
    public String toString() {
        return "{" + n + '}';
    }
    @Override
    protected Integer compute() {
        // 如果 n 已经为 1,可以求得结果了
        if (n == 1) {
            log.debug("join() {}", n);
            return n;
        }

        // 将任务进行拆分(fork)
        AddTask1 t1 = new AddTask1(n - 1);
        t1.fork();
        log.debug("fork() {} + {}", n, t1);

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

  这样用二分的思想,从中间开始拆分,调用链就是这样了:

  可以看到t3和t2是可以并行的,这样就能提高cpu资源利用率

 

posted @ 2021-08-20 11:27  艾尔夏尔-Layton  阅读(179)  评论(0编辑  收藏  举报