Fork/Join框架

来源:互联网 发布:java中方法什么意思啊 编辑:程序博客网 时间:2024/06/01 09:37

Fork/Join框架的思想

Fork/Join的思想是一个分而治之的思想的实现,跟MapReduce的思想如出一辙,简单的说就是加入我们需要处理1000个数据,但是我们并不具备处理1000个数据的能力,那么我们可以让一个线程只处理其中的10个,然后分阶段处理100次,将100次的结果进行合并,那么就会得到对最终的1000个数据的处理结果

JDK并发包中的实现

在实际应用中,我们没有办法毫无顾忌地使用fork()开启线程进行处理,假使我们这么做了,那么很有可能导致系统开启过多的线程而严重影响性能。所以在JDK中,给出了一个ForkJoinPool的线程池,对于fork()方法并不急着开启线程,而是提交给ForkJoinPool线程池进行处理,以节省系统资源。
ForkJoinPool的submit方法:

public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task)

这个方法需要一个ForkJoinTask类型的参数,ForkJoinTask就是支持fork()分解和join()等待的任务。ForkJoinTask是一个抽象类,它有两个子类,他们分别表示没有返回值的任务和有返回值的任务

RecursiveActionRecursiveTask<V>

图解Fork/Join框架:
图解Fork/Join框架

案例

计算若干个数值的和

public class CountTask extends RecursiveTask<Long>{    //每次相加的范围是1000个数    private static final Long THRESHOLD = 10L;    private long start;    private long end;    public CountTask(long start, long end) {        super();        this.start = start;        this.end = end;    }    @Override    protected Long compute() {        long sum = 0;        boolean canCompute = (end-start)<THRESHOLD;        if(canCompute){            System.out.println(start+"\t"+end);            for(long i =start;i<=end;i++){                sum+=i;            }        }else{            //分成若干个小任务            long step = (end-start+1)/10;            List<CountTask> subTasks = new ArrayList<>();            for(int i=0;i<Math.ceil((end-start+1)/10.0);i++){                CountTask countTask = null;                if((start+step*(i+1)-1)>end){                    countTask = new CountTask(start+step*i, end);                }else{                    countTask = new CountTask(start+step*i, start+step*(i+1)-1);                }                subTasks.add(countTask);                countTask.fork();            }            for(CountTask c:subTasks){                sum+=c.join();            }        }        return sum;    }    public static void main(String[] args) throws Exception {        ForkJoinPool forkJoinPool = new ForkJoinPool();        CountTask countTask = new CountTask(1, 101);        ForkJoinTask<Long> forkJoinTask = forkJoinPool.submit(countTask);        System.out.println(forkJoinTask.get());    }}

新建一个可以fork()/join()的任务,由于是计算总和,所以这里我们需要有返回值的任务,这里就是继承RecursiveTask
重写compute方法,在这个里面对任务进行分解
针对每一个分解的任务,进行fork操作
针对每一个被分解的任务进行join操作,等待任务执行完毕后的数据的汇总
注意
由于我们这里边使用的是递归的方式,所以我们需要注意不能将任务划分的过深,否则会造成下面的问题:
* 系统内的线程越积越多,导致性能的严重下降
* 函数的调用层次(递归的层次)过多,导致栈溢出