编程语言
首页 > 编程语言> > 【java笔记】java中线程池之ForkJoinPool的原理及使用

【java笔记】java中线程池之ForkJoinPool的原理及使用

作者:互联网

本文参考自CSDN作者 YourBatman 的ForkJoinPool线程池的使用以及原理和知乎作者 欣然 的文章高并发之Fork/Join框架使用及注意事项

1. 实例

计算从1到100000的累计和:

package ecnu.cn;

import java.util.concurrent.*;

public class SumTask extends RecursiveTask<Long> {
    private int start, end;

    public static void main(String[] args) {
        SumTask sumTask = new SumTask(1, 100000);
        ForkJoinPool pool = new ForkJoinPool();
        Future<Long> result = pool.submit(sumTask);
        try {
            System.out.println(result.get());;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public SumTask(int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        Long sum = 0L;
        if (end - start < 10) {
            for (int i = start; i <= end; i++) {
                sum += i;
            }
        } else {
            int middle = (start + end) / 2;
            SumTask taskLeft = new SumTask(start, middle);
            SumTask taskRight = new SumTask(middle + 1, end);
            // 执行子任务
            taskLeft.fork();
            taskRight.fork();
            // 等待任务执行结束
            Long leftResult = taskLeft.join();
            Long rightResult = taskRight.join();
            sum = leftResult + rightResult;
        }
        return sum;
    }
}

2. fork和join、submit和invoke、RecursiveTask和RecursiveAction

(1) 每个线程执行时

也可以将上述代码中的

taskLeft.fork();
taskRight.fork();

改为:

invokeAll(leftTask, rightTask);

两者的区别在于,对于Fork/Join模式,假如Pool里面线程数量是固定的,那么调用子任务的fork方法相当于A先分工给B,然后A当监工不干活,B去完成A交代的任务。所以上面的模式相当于浪费了一个线程。那么如果使用invokeAll相当于A分工给B后,A和B都去完成工作。这样可以更好的利用线程池,缩短执行的时间。 所以使用invokeAll更优。

(2) ForkJoinPool提交时

(3) 类在继承时

3. 原理

在线程创建时,每个fork()并不都会生成一个新的线程,而每个join()也不一定会造成线程阻塞,而是采用了一种更加复杂的算法——工作切取(work stealing),其流程如下:

标签:fork,程池,java,SumTask,ForkJoinPool,start,任务,线程
来源: https://blog.csdn.net/zuzhiang/article/details/118273080