解析 Stream foreach 源码
本文分享自华为云社区《深入理解Stream之foreach源码解析》,作者:李哥技术 。
前言
前面在深入理解 Stream 之原理剖析中提到,Stream 中的操作可以分为两大类:中间操作与结束操作。
今天要说的 foreach 是属于结束操作。
Stream 流操作从并发上来分类,又可以分为并行流和串行流,今天就来刨根问底的看看吧。
foreach 串行流
foreach 并行流
从目前来看,parallelStream(并行流)与 stream(串行流)的区别仅仅是一个变量而已。别着急,让我们继续往下看。
记住这里的 ordered 是 false,因为是并行流,不可能是有序遍历。continue。
我们来简单分析一下 ForEachTask 类,它继承于 CountedCompleter。
最后这里使用 ForkJoin 框架,利用分治法的思想,将一个大任务拆分很多个小任务去执行,最后一一汇总到大任务。
我们一路过五关斩六将,终于将它给挖穿了。不容易啊。
总结
我们简单回顾总结一下:
对于串行流:
先得到 ReferencePipeline.Head 的 Stream 实现类,内部有一个拆分器,值是一个 ArrayListSpliterator 对象;
对于并行流,当前线程直接调用 ArrayListSpliterator 对象的 forEachRemaining 方法。
对于并行流:
先得到 ReferencePipeline.Head 的 Stream 实现类,内部有一个拆分器,值是一个 ArrayListSpliterator 对象;
迭代的时候调用父类的 forEach 方法;
构建一个 ForEachTask,当前线程继续执行 invoke 方法;
最终执行 java.util.stream.ForEachOps.ForEachTask#compute 方法,使用 ForkJoin 框架,利用 commomPool、ForkJoin 框架分治法的思想,使用拆分器将任务拆分成不同子任务执行;
对于每一个子任务都会拆分到不能再拆分为止,然后调用 java.util.stream.AbstractPipeline#copyInto 方法,在内部会调用不可再拆分的拆分器的 forEachRemaining 方法,最终调用回调用户方法 action.accept(e);
串行流比较简单,对于 parallelStream,站在它背后的男人是 ForkJoin 框架。
ForkJoin 框架是从 jdk7 中新特性,它同 ThreadPoolExecutor 一样,也实现了 Executor 和 ExecutorService 接口。ForkJoinPool 主要用来使用分治法(Divide-and-Conquer Algorithm)来解决问题。Java 8 为 ForkJoinPool 添加了一个通用线程池:commonPool,这个线程池用来处理那些没有被显式提交到任何线程池的任务。它是 ForkJoinPool 类型上的一个静态元素,它拥有的默认线程数量等于运行计算机上的处理器数量。所以,我们的并行流就是使用的这个公共池中的线程来执行的。
版权声明: 本文为 InfoQ 作者【华为云开发者联盟】的原创文章。
原文链接:【http://xie.infoq.cn/article/55c0b8741afb62a1f0a35849d】。文章转载请联系作者。
评论