Stream中foreach方法源码解析如何深入理解?

2026-05-29 13:074阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计944个文字,预计阅读时间需要4分钟。

Stream中foreach方法源码解析如何深入理解?

家好,我是李哥。JDK1.6和JDK1.8是主流的两个版本,目前市场上使用最广泛的是JDK1.8。因此,我们有必要探讨一下Java 8的一些新特性。深入理解Lambda的奥秘,以及Stream原理。

大家好,我是李哥。

JDK1.6和JDK1.8是主流的两个大版本,目前市场上用的最多最多的依然是JDK1.8。所以,我们有必要聊一聊Java8的一些新特性。

  • 深入理解lambda的奥秘
  • 深入理解Stream之原理剖析
  • 深入理解Stream之foreach源码解析
  • 深入浅出NPE神器Optional
  • 谈谈接口默认方法与静态方法
  • 深入浅出重复注解与类型注解
  • 深入浅出JVM元空间metaspace
  • 深入理解CompletableFuture异步编程
  • 今天我们先来聊聊深入理解Stream之foreach源码解析。

    前言

    前面在深入理解Stream之原理剖析中提到,Stream中的操作可以分为两大类:中间操作与结束操作。

    今天要说的foreach是属于结束操作。

    Stream中foreach方法源码解析如何深入理解?

    Stream流操作从并发上来分类,又可以分为并行流和串行流,今天就来刨根问底的看看吧。

    foreach串行流

    foreach并行流

    从目前来看,parallelStream(并行流)与stream(串行流)的区别仅仅是一个变量而已。别着急,让我们继续往下看。

    记住这里的ordered是false,因为是并行流,不可能是有序遍历。continue。

    我们来简单分析一下ForEachTask类,它继承于CountedCompleter。

    static final class ForEachTask<S, T> extends CountedCompleter<Void> {
    }

    最后这里使用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类型上的一个静态元素,它拥有的默认线程数量等于运行计算机上的处理器数量。所以,我们的并行流就是使用的这个公共池中的线程来执行的。

    对于ForkJoin框架,我们今天就说这么多,如果还想了解更多细节,后面我会针对高并发多线程出一个系列,敬请期待!

    本文共计944个文字,预计阅读时间需要4分钟。

    Stream中foreach方法源码解析如何深入理解?

    家好,我是李哥。JDK1.6和JDK1.8是主流的两个版本,目前市场上使用最广泛的是JDK1.8。因此,我们有必要探讨一下Java 8的一些新特性。深入理解Lambda的奥秘,以及Stream原理。

    大家好,我是李哥。

    JDK1.6和JDK1.8是主流的两个大版本,目前市场上用的最多最多的依然是JDK1.8。所以,我们有必要聊一聊Java8的一些新特性。

  • 深入理解lambda的奥秘
  • 深入理解Stream之原理剖析
  • 深入理解Stream之foreach源码解析
  • 深入浅出NPE神器Optional
  • 谈谈接口默认方法与静态方法
  • 深入浅出重复注解与类型注解
  • 深入浅出JVM元空间metaspace
  • 深入理解CompletableFuture异步编程
  • 今天我们先来聊聊深入理解Stream之foreach源码解析。

    前言

    前面在深入理解Stream之原理剖析中提到,Stream中的操作可以分为两大类:中间操作与结束操作。

    今天要说的foreach是属于结束操作。

    Stream中foreach方法源码解析如何深入理解?

    Stream流操作从并发上来分类,又可以分为并行流和串行流,今天就来刨根问底的看看吧。

    foreach串行流

    foreach并行流

    从目前来看,parallelStream(并行流)与stream(串行流)的区别仅仅是一个变量而已。别着急,让我们继续往下看。

    记住这里的ordered是false,因为是并行流,不可能是有序遍历。continue。

    我们来简单分析一下ForEachTask类,它继承于CountedCompleter。

    static final class ForEachTask<S, T> extends CountedCompleter<Void> {
    }

    最后这里使用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类型上的一个静态元素,它拥有的默认线程数量等于运行计算机上的处理器数量。所以,我们的并行流就是使用的这个公共池中的线程来执行的。

    对于ForkJoin框架,我们今天就说这么多,如果还想了解更多细节,后面我会针对高并发多线程出一个系列,敬请期待!