java中Stream、ParallelStream和ForkjoinPool

目录​​​​​​​​​​​​​​

 

Stream、ParallelStream和ForkjoinPool简述 

一、 Stream的原理

二、 ParallelStream的优势与坑点

forkjoin框架中的任务队列参考

三 ParallelStream最佳实践

四 理解parallelStream执行的小实验

参考文档


Stream、ParallelStream和ForkjoinPool简述 

stream是java8开始引入的流式编程

parallelStream是在流式编程的基础上并行化流水线上可以并行化的部分(StatelessOp)

fork-join-pool是parallelStream并行化的底层框架,是独立于ThreadPool的多线程并行模型。

三者虽然在功能上关系紧密,但掌握他们要了解的东西却互相独立

一  Stream的原理

参考. 深入理解Java8中Stream的实现原理_lcgoing的博客-CSDN博客_java stream流原理目录一.容器执行Lambda表达式的方式1.回顾2.Stream API二.Stream的实现原理1.一种直白的实现方式2.Stream流水线解决方案1).操作如何记录2).操作如何叠加3).叠加之后的操作如何执行4).执行后的结果在哪里一.容器执行Lambda表达式的方式1.回顾首先回顾一下容器执行Lambda表达式的方式,以ArrayList...https://blog.csdn.net/lcgoing/article/details/87918010

二 ParallelStream的优势与坑点

parallelStream底层依赖ForkJoin并行框架。

forkjoin框架的特点是分治,把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果。这个结构和Stream流水线可以很好地结合。

默认的parallelStream底层依赖同一个线程池,默认线程数为机器CPU数量-1。(推荐独立的任务使用parallelStream时使用自定义的forkjoinpool,避免预期外的parallelStream对同一个底层线程池的影响。最佳实践会强调)。fork-join框架下,线程池相的线程数量对有限,但是几乎不限容量(2的64次方)的工作队列是根据分治算法分片分配的,上百万的子任务可以插入到工作队列,由线程池中的线程进行处理。为应对有的队列提前处理完成,fork-join中有工作窃取的处理机制。详见下图中参考文档。

图片来自。及fork-join框架可参考文章聊聊并发(八)——Fork/Join框架介绍-InfoQ

forkjoin框架中的任务队列参考

ForkJoinPool分析之任务队列 - MaXianZhe - 博客园本文参考自 https://blog.csdn.net/tyrroo/article/details/81483608 public class ForkJoinPool extends Ahttps://www.cnblogs.com/juniorMa/articles/14234472.html

三 ParallelStream最佳实践

Java中自定义ForkJoinPool使用的方式较为冗余丑陋,会影响代码的可读性;在非必要的情况下,尽量使用ThreadPool。

IO密集型操作,使用ThreadPool而不是Forkjoin、parallelStream。

避免在没有自定义ForkJoinPool的情况下使用Collection.parallelStream()Stream.parallel()。因为共用CommonPool可能导致多个独立的业务操作之间相互争抢线程以至相互影响。优先避免使用,如果一定要用,请指定特定的ForkJoinPool。

JDK内置的CommonPool内线程初始化时机可能非常早,相应线程上绑定的ClassLoader可能并非应用真正使用的ClassLoader,这对于依赖特定ClassLoader加载类的框架来说,可能出现意外的找不到类问题。

四 理解parallelStream执行的小实验

package hcity.concurrency;

import com.google.common.collect.Lists;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.apache.commons.lang3.RandomUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.Collectors;

public class ForkJoinTest {
    ForkJoinPool forkJoinPool = new ForkJoinPool(3);
    ForkJoinPool forkJoinPool2 = new ForkJoinPool(9);
    Map<String, Integer> mem = new HashMap<>();

    void test1() {
        List<Integer> arrs = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9);

        System.out.println("===============  实验1:核心线程为3的forkJoin执行情况。观察线程数和执行情况  ===============");
        List<InnerTest01> ins = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            InnerTest01 test01 = new InnerTest01(i + "");
            ins.add(test01);
        }
        long start = System.currentTimeMillis();
        forkJoinPool.submit(() -> ins.parallelStream().forEach(this::addOp)).join();
        System.out.println("耗时 : " + (System.currentTimeMillis() - start));
//        ins.forEach(System.out::println);
        System.out.println("===============  实验1:结束  ===============\n");

        // 根据自己喜好设定实验间隔时间。或者改写单独执行实验。
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("===============  实验2:串行执行,比较耗时  ===============");
        System.out.println("实验1中每个工作线程的总睡眠时间:" + mem); //
        start = System.currentTimeMillis();
        List<Integer> ans3 = ins.stream().map(this::addOp).collect(Collectors.toList());
        System.out.println("耗时" + (System.currentTimeMillis() - start));
        System.out.println(ans3);
        System.out.println("===============  实验2:结束  ===============\n");

        System.out.println("===============  实验3:两个自定义forkJoinPool运行。观察线程数和执行情况  ===============");
        start = System.currentTimeMillis();
        ForkJoinTask<List<Integer>> task1 = forkJoinPool.submit(() -> arrs.parallelStream().map(x -> addOp(x, forkJoinPool)).collect(Collectors.toList()));
        ForkJoinTask<List<Integer>> task2 = forkJoinPool2.submit(() -> arrs.parallelStream().map(x -> addOp(x, forkJoinPool2)).collect(Collectors.toList()));

        List<Integer> ans = task1.join();
        List<Integer> ans2 = task2.join();
        System.out.println("耗时" + (System.currentTimeMillis() - start));
        System.out.println(ans);
        System.out.println(ans2);
        System.out.println("===============  实验3:结束。  ===============");
    }

    private Integer addOp(int x, ForkJoinPool forkJoinPool) {
        Thread t = Thread.currentThread();
        String name = t.getName();
        int stime = RandomUtils.nextInt(200, 1000);
        System.out.println("示例1 Thread:" + name + " value:" + x + " sleep time " + stime + " activeCount :" + forkJoinPool.getActiveThreadCount() + " queueSize " + forkJoinPool.getQueuedTaskCount());
        try {
            Thread.sleep(stime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return x + 1;
    }

    public static void main(String[] args) {
        ForkJoinTest fj = new ForkJoinTest();
        fj.test1();
    }

    @Getter
    @Setter
    @ToString
    static
    class InnerTest01 {
        String a;

        InnerTest01(String a) {
            this.a = a;
        }
    }

    int addOp(InnerTest01 test01) {
        Thread t = Thread.currentThread();
        String name = t.getName();
        int stime = RandomUtils.nextInt(500, 1000);
        System.out.println("示例0 Thread:" + name + " value:" + test01.getA() + " sleep time " + stime + " activeCount :" + forkJoinPool.getActiveThreadCount() + " queueSize " + forkJoinPool.getQueuedTaskCount());
        mem.put(name, mem.getOrDefault(name, 0) + stime);
        try {
            Thread.sleep(stime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int newA = Integer.parseInt(test01.getA()) + 1;

        test01.setA(String.valueOf(newA));
        return newA;
    }
}

参考文档

深入理解Java8中Stream的实现原理_lcgoing的博客-CSDN博客_java stream流原理

聊聊并发(八)——Fork/Join框架介绍-InfoQ

ForkJoinPool分析之任务队列 - MaXianZhe - 博客园​​​​​​​

JUC源码分析-线程池篇(四):ForkJoinPool - 1 - 简书

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值