分而治之:Fork/Join框架

来源:互联网 发布:java web框架最新技术 编辑:程序博客网 时间:2024/05/24 00:14

Fork/Join框架的介绍

Fork/Join框架是Java7提供了的一个用于并发执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架

Fork/Join执行逻辑
这里写图片描述

工作窃取算法

工作窃取算法是指某个线程从其他队列中窃取任务来执行
在一般情况下,一个物理线程实际需要处理多个逻辑任务,因此每个线程必然需要拥有一个任务队列。比如,线程A已经把自己所以任务都执行完了,而线程B还有一堆任务没有完成,那么线程A就会帮助线程B,从线程B中拿一些任务过来处理,尽可能达到平衡,总是从任务队列的底部拿数据,而线程执行自己的任务是从相反的顶部开始拿。这样就有利于避免数据竞争

使用Fork/Join框架

第一步首先分割任务,需要一个fork类来把大任务分割成小任务,如果子任务还是很大,那继续分割直到足够小
第二步然后合并任务结果,分割子任务分别放在双端队列,然后几个启动线程分别从双端队列中获取任务执行,子任务执行完的结果统一放在一个队列里,启动一个线程从队列里拿数据,然后合并这些数据。

常用方法
compute(); 计算方法(分拆的子任务)
fork(); // 执行子任务
join(); // 子任务结束后返回对应结果

import java.util.ArrayList;import java.util.concurrent.Callable;import java.util.concurrent.ForkJoinPool;import java.util.concurrent.ForkJoinTask;import java.util.concurrent.RecursiveTask;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;    }    public Long compute() {        long sum = 0;        //如果需要求和的总数大于THRESHOLD,那么任务继续分解,否则直接可以执行了        boolean canCompute = (end - start) < THRESHOLD;        if (canCompute) {            for (long i = start; i < end; i++) {                sum += i;            }        } else {            // 分成100任务            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 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 (Exception e) {        }    }}

使用ForkJoin框架经常使用到两个任务模型RecursiveTask和RecursiveAction,RecursiveTask用于定义有返回值的任务,RecursiveAction用于定义没有返回值的任务,首先建立ForkJoinPool线程池,再构造一个计算1到200000求和的任务。将任务提交给线程池,线程池会返回一个携带结果的任务,通过get()获取最终结果,如果调用get()时候任务还没完成就等待。

如果任务的划分层次过深会出现两种情况:
1、系统内的线程数量越积越多,导致性能下降
2、函数调用层次很深,导致栈溢出

参考:《Java高并发程序设计》

0 0
原创粉丝点击