前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >多线程—ForkJoinPool

多线程—ForkJoinPool

作者头像
只喝牛奶的杀手
发布2019-08-26 16:55:25
6080
发布2019-08-26 16:55:25
举报

为什么要池化?提高复用率,提高效率。

为什么要用ForkJoinPool?ForkJoinPool的优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”,把多个“小任务”放到多个处理器核心上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来即可。——《疯狂Java讲义》

比如随着业务的数据越来越多,它会自动的分越来越多的task;

Fork/Join框架是用来解决能够通过分治技术(Divide and Conquer Technique)将问题拆分成小任务的问题。在一个任务中,先检查将要解决的问题的大小,如果大于一个设定的大小,那就将问题拆分成可以通过框架来执行的小任务。如果问题的大小比设定的大小要小,就可以直接在任务里解决这个问题,然后,根据需要返回任务的结果。下面的图形总结了这个原理。

没有固定的公式来决定问题的参考大小(Reference Size),从而决定一个任务是需要进行拆分或不需要拆分,拆分与否仍是依赖于任务本身的特性。可以使用在任务中将要处理的元素的数目和任务执行所需要的时间来决定参考大小;

THRESHOLD 阈值的大小根据最小业务单元不超时来定,随着业务数据的增加会分越来越多的Task,一般根据裂项法。

创建ForkJoinPool并提交任务

代码语言:javascript
复制
    @org.junit.Test
    public static void main(String args[]) throws ExecutionException, InterruptedException {
        List<Integer> shopIds = new ArrayList<>();
        for (int i = 0; i <100; i++) {
            shopIds.add(i);
        }
        long bt = System.currentTimeMillis();
        for(Integer id:shopIds){
            System.out.println(id);
        }
        System.out.println("for循环时间" + (System.currentTimeMillis() - bt) + "ms");
        long beforeTime = System.currentTimeMillis();
        int poolSize = Runtime.getRuntime().availableProcessors();
        ForkJoinPool pool = new ForkJoinPool(poolSize);
        System.out.println("tNumber = " + pool.getParallelism());
        ForkJoinTask<Integer> forkJoinTask = pool.submit(new TestService(shopIds, 0, shopIds.size()));
        System.out.println(forkJoinTask.get());
        System.out.println(forkJoinTask.isDone());
        System.out.println("Total Time in MilliSecond Taken ->  " + (System.currentTimeMillis() - beforeTime) + "ms");
}

如果需要传值,通过构造函数去传值。RecursiveAction:没有返回值,只是执行任务,RecursiveTask:有返回值,小任务结束后,返回结果。大任务可将小任务返回结果进行整合。

定义RecusiveTask(RecursiveAction)

代码语言:javascript
复制
public class TestService extends RecursiveTask<Integer> {
    private static final int THRESHOLD = 20;
    private final List<Integer> shopIds;
    private int startIndex;
    private int endIndex;
 
    public TestService(List<Integer> shopIds, int startIndex, int endIndex) {
        this.shopIds = shopIds;
        this.startIndex = startIndex;
        this.endIndex = endIndex;
    }
 
    @Override
    protected Integer compute() {
        System.out.println("startIndex:" + startIndex + " endIndex:" + endIndex);
        if (endIndex - startIndex < THRESHOLD) {
            long beforeTime = System.currentTimeMillis();
            List<Integer> ids = shopIds.subList(startIndex, endIndex);
            System.out.println(ids.size());
            for (Integer shopId : ids) {
                System.out.println(Thread.currentThread().getName() + "*********************打印" + shopId);
            }
            System.out.println("测试案例 " + (System.currentTimeMillis() - beforeTime) + "ms");
        } else {
            int middleIndex = (startIndex + endIndex) / 2;
            new TestService(shopIds, startIndex, middleIndex).fork();
            new TestService(shopIds, middleIndex, endIndex).fork();
            System.out.println(Thread.currentThread().getName() + "*********************拆分");
        }
        return null;
    }
}

实测下来,当路径足够复杂时,ForkJoinPool的优势会愈加明显。但是,就像快排一样,最优策略并不是一个思路走到死,当分治的区域较小时,可以将小区域改用插入排序进行排序。同理,当我们递归到情况不再复杂时,就可以转而用别的线程池进行处理。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-03-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 只喝牛奶的杀手 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档