Java8中怎么正确高效的使用并行流
Java8中怎么正确高效的使用并行流
这篇文章主要为大家展示了“Java8中怎么正确高效的使用并行流”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Java8中怎么正确高效的使用并行流”这篇文章吧。
正确使用并行流,避免共享可变状态
错用并行流而产生错误的首要原因,就是使用的算法改变了某些共享状态。下面是另一种实现对前n个自然数求和的方法,但这会改变一个共享累加器:
publicstaticlongsideEffectSum(longn){Accumulatoraccumulator=newAccumulator();LongStream.rangeClosed(1,n).forEach(accumulator::add);returnaccumulator.total;}publicclassAccumulator{publiclongtotal=0;publicvoidadd(longvalue){total+=value;}}
有什么问题呢?
它在本质上就是顺序的。每次访问 total 都会出现数据竞争。如果用同步来修复,那就完全失去并行的意义了。
为了说明这一点,让我们试着把 Stream 变成并行的:
publicstaticlongsideEffectParallelSum(longn){Accumulatoraccumulator=newAccumulator();LongStream.rangeClosed(1,n).parallel().forEach(accumulator::add);returnaccumulator.total;}
测试下,输出
性能无关紧要了,唯一要紧的是每次执行都会返回不同的结果,都离正确值差很远。这是由于多个线程在同时访问累加器,执行 total += value ,而这却不是一个原子操作。问题的根源在于, forEach 中调用的方法有副作用它会改变多个线程共享的对象的可变状态。
要是你想用并行 Stream 又不想引发类似的意外,就必须避免这种情况。
所以共享可变状态会影响并行流以及并行计算,要避免共享可变状态,确保并行 Stream 得到正确的结果。
高效使用并行流
是否有必要使用并行流?
如果有疑问,多次测试结果。把顺序流转成并行流轻而易举,但却不一定是好事
留意装箱。自动装箱和拆箱操作会大大降低性能
Java 8中有原始类型流( IntStream 、LongStream 、 DoubleStream )来避免这种操作,但?有可能都应该用这些流。
有些操作本身在并行流上的性能就比顺序流差。特别是 limit 和 findFirst 等依赖于元素顺序的操作,它们在并行流上执行的代价非常大。
例如, findAny 会比 findFirst 性能好,因为它不一定要按顺序来执行。可以调用 unordered 方法来把有序流变成无序流。那么,如果你需要流中的n个元素而不是专门要前n个的话,对无序并行流调用limit 可能会比单个有序流(比如数据源是一个 List )更高效。
还要考虑流的操作流水线的总计算成本。
设N是要处理的元素的总数,Q是一个元素通过流水线的大致处理成本,则N*Q就是这个对成本的一个粗略的定性估计。Q值较高就意味着使用并行流时性能好的可能性比较大。
对于较小的数据量,选择并行流几乎从来都不是一个好的决定。并行处理少数几个元素的好处还?不上并行化造成的额外开销
要考虑流背后的数据结构是否易于分解。
例如, ArrayList 的拆分效率比 LinkedList高得多,因为前者用不着遍历就可以平均拆分,而后者则必须遍历。另外,用 range 工厂方法创建的原始类型流也可以快速分解。
流自身的特点,以及流水线中的中间操作修改流的方式,都可能会改变分解过程的性能。
例如,一个 SIZED 流可以分成大小相等的两部分,这样每个部分都可以比较高效地并行处理,但筛选操作可能丢弃的元素个数却无法预测,导致流本身的大小未知。
还要考虑终端操作中合并步骤的代价是大是小(例如 Collector 中的 combiner 方法)
如果这一步代价很大,那么组合每个子流产生的部分结果所付出的代价就可能会超出通过并行流得到的性能提升。
流的数据源和可分解性
最后, 并行流背后使用的基础架构是Java 7中引入的分支/合并框架了解它的内部原理至关重要。
以上是“Java8中怎么正确高效的使用并行流”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注恰卡编程网行业资讯频道!
推荐阅读
-
Java8如何使用CompletableFuture构建异步应用方式
-
基于Java8并行流有哪些需要注意的地方
这篇文章主要介绍了基于Java8并行流有哪些需要注意的地方,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大...
-
Java8中怎么将Array转换为Stream
本篇文章给大家分享的是有关Java8中怎么将Array转换为Stream,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇...
-
Java8中有哪些常用的时间api
这篇文章给大家介绍Java8中有哪些常用的时间api,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Insta...
-
如何在Java 8中引入lambda表达式
本篇文章给大家分享的是有关如何在Java8中引入lambda表达式,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章...
-
怎么在Java8怎么在Map中新增数据
这期内容当中小编将会给大家带来有关怎么在Java8怎么在Map中新增数据,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文...
-
怎么在java8项目中对List对象属性去重
怎么在java8项目中对List对象属性去重?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面...
-
怎么在Java8中给forEach()函数提供index值
怎么在Java8中给forEach()函数提供index值?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细...
-
DateTimeFormatter与SimpleDateFormat在Java8中有什么区别
今天就跟大家聊聊有关DateTimeFormatter与SimpleDateFormat在Java8中有什么区别,可能很多人都不太...
-
Java8中groupBy实现集合的分组
这篇文章主要介绍Java8中groupBy实现集合的分组,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!场景...