gpt4 book ai didi

java-8 - 并行流Java总结

转载 作者:行者123 更新时间:2023-12-01 04:36:12 27 4
gpt4 key购买 nike

Java并行流将始终并行执行每个操作并返回相同的结果吗?

例如。

    IntStream of = IntStream.of(1, 2, 3);
of = of.parallel();

int reduce = of.reduce(0, (a,b) -> a + b);
System.out.println("Result: " + reduce);


这将始终返回6吗?

最佳答案

我将尽力回答您的所有问题:


关于您的示例:of.reduce(0, (a,b) -> a + b);


这将总是返回相同的结果吗? (...总是返回6?)=>是
这将始终并行执行吗? =>否

关于Java Streams上的所有操作


他们将始终返回相同的结果吗? =>否
他们将始终并行执行吗? =>否



#1.快速答案#

1.1关于您的示例:of.reduce(0, (a,b) -> a + b);

1.1.1这将总是返回相同的结果吗?

是的,无论您多久运行一次程序,它都将始终产生相同的结果。只要您实现了JVM,并且您的计算机(硬件和OS)运行正常。

Java只是asks you to use an associative operation(您提供了(+))和整数集的标识(您提供了0)。背景部分中的更多信息。

1.1.2是否总是并行执行?

如果用并行表示真正并行(同时进行不同的计算),那么可以用一个明确的否定答案来回答这部分问题。


如果,例如您只有一个线程,那么您的硬件就无法执行真正的并行计算。
操作系统上的JVM可能仅支持用户级线程。即使不是真正的多线程,这也是实现多线程行为的一种非常可移植的方法。含义如果JVM仅使用此类“绿色线程”,则即使您有多个CPU,也无法并行执行这些线程,因为您的内核不知道任何其他线程。关于here的一些其他Wiki信息。但是正如Wiki所指出的那样,在JVM的较新实现中,绿色线程的实现并不常见-从this SO answer开始,只有Squawk虚拟机才是最近的例外。
另一件事是,可能多个线程用于计算三个整数会产生过多的开销。因此,JVM可能只说:

“好吧,他要我并行计算这个吗?!真的吗?!……不,我只是顺序地计算这个,因为为这种小的计算创建额外的线程太昂贵了!”


另一方面,如果您确实具有必要的硬件,并且计算非常困难,以致JVM无法优化多线程处理,那么可以,它将始终并行进行计算。

1.2关于Java Streams上的所有操作

1.2.1他们将始终返回相同的结果吗?

不,这取决于您使用的操作和数据结构。可能会有副作用,状态表达式和排序问题。

有关更多信息,请参见此答案中的2.3.2:并行流上的其他操作。

1.2.2他们将始终并行执行吗?

不,这里还是关于您的示例部分中所写的要点:这将始终并行执行吗?

另外,您可以实现或使用其他数据结构并定义自己的收集操作,依此类推:

如果您使用的数据结构过于同步,那么即使使用多个线程,您的计算实际上也可能是按顺序进行的。那是一个线程阻止所有其他线程继续计算直到完成。

#2背景#

一般情况的介绍和示例将介绍reduce的功能以及为什么在每种情况下都能成功产生相同的结果。

特定而言,Java添加了一些信息,这些信息对于实施特定于Java的归约是正确的。最后,除了reduce之外,还有其他操作信息。

2.1简介

我建议这将取决于您使用的功能以及将此功能应用于的上下文(对象集)。

在您的情况下,您可以将函数(+)与整数(1,2,3)结合使用。

(+)在整数集中有一些规则:


a + b = b + a
a +(b + c)=(a + b)+ c


因此,在一般情况下,这些规则(和其他规则)使归约始终可以在每种情况下都产生相同的结果,而与归约(正确)的实现无关。

2.2一般案例

您已经获得函数(+)和有序序列:(1,2,3)。

假设有3个线程:


收集器线程将(1,3)分配给线程1,(2,0)分配给线程2。
线程1计算:1 + 3并将4返回到收集器线程。
线程2计算:2 + 0并将2返回到收集器线程。
收集器线程计算:4 + 2 = 6并将其作为结果返回。


在这个一般示例中,收集器的运行是无序的,这意味着,他并不关心序列中元素的顺序,而是随机分配计算结果,还只是按照子线程完成结果的顺序合并结果。

线程可以按这些顺序执行这些计算,对于整数集中可以应用到函数(+)的规则而言,不会有任何区别。

2.3特定于Java

2.3.1 someStream.reduce(identity,someOrderedSequence)

正如Holger在评论中所述,可交换属性不是必需的。

与我的*“一般情况下的示例”相反,订购了使用Java进行此操作的收集器。因此,它确实在乎所提供序列的顺序。它将按顺序分配范围,并按正确的顺序收集结果。

因为是这种情况,所以Java的IntStream类对对象集和函数的属性的限制较少:

该函数只必须具有关联属性,即(+):a + (b + c) = (a + b) + c

因此最后,结果的明确性仅取决于关联属性,因为Java的约简是为了按顺序使用流的元素和计算结果而实现的。

java documentation


int reduce(int身份,IntBinaryOperator op)

使用提供的标识值和
关联累加函数,并返回减小的值。


2.3.2并行流上的其他操作

介绍

除了特定的reduce(...)函数,您的问题还包括有关一般情况的查询:


Java并行流将始终(...)每个操作(...)返回相同的结果吗?


问题的这一部分可以用“否”回答,因为它取决于操作和执行操作所依据的数据结构。

有很好的一般descriptions on using Streams in the Java specs

有状态

Java文档本身提供了an example of a stateful lambda expression on parallel streams

 Set<Integer> seen = Collections.synchronizedSet(new HashSet<>());
stream.parallel().map(e -> { if (seen.add(e)) return 0; else return e; })...



在此,如果并行执行映射操作,则结果
由于线程的原因,同一输入的运行时间可能会有所不同
安排差异。


副作用


作为如何转换流管道的示例
不适当地将副作用不适用于不这样做的副作用,以下内容
代码在字符串流中搜索与给定常规匹配的字符串
表达式,然后将匹配项放在列表中。


 ArrayList<String> results = new ArrayList<>();
stream.filter(s -> pattern.matcher(s).matches())
.forEach(s -> results.add(s)); // Unnecessary use of side-effects!



此代码不必要地使用了副作用。如果并行执行,
ArrayList的非线程安全性将导致错误的结果,...


定购

在写有关Java的特定实现的文章时,我已经谈到过排序,它只需要关联操作即可始终返回相同的结果。

顺序很重要!您提供的示例适用于有序的整数序列,并使用有序的收集器,因此始终创建相同的结果。它是否实际上是在后台并行执行任务。

另一方面,如果您使用无序流和/或无序收集器,则这些诺言将不再成立。请参见此引用 from the Java specs


如果订购了流,则大多数操作都被限制在
遇到顺序中的元素;如果流的来源是
包含[1、2、3]的列表,然后是执行map(x-> x * 2)的结果
必须为[2,4,6]。但是,如果源没有定义的相遇
顺序,则值[2、4、6]的任何排列都是有效的
结果。


因此,您可以再次考虑一般情况下的示例。这里指出,即使序列和收集器是无序的,结果每次也必须相同,因为 (+)在整数集合中也具有可交换性。

#3结论#

总而言之,Java是否真的在Streams上进行真正的并行执行,并且始终返回相同的结果取决于许多因素。因此,通常不会有(是/否)答案。

如果您有一组假设,可以将其作为问题的前提条件,那么答案可能会更直接(例如,特定的硬件功能,始终执行真正的并行性的JVM,始终保持有序的数据结构,...) 。

关于java-8 - 并行流Java总结,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45302851/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com