聚合并行性
并行计算涉及将问题划分为子问题,同时解决这些问题(并行地,每个子问题运行在一个单独的线程中),然后将子问题的解决结果组合起来。Java SE提供了fork/join框架,它使你能够更容易地在应用程序中实现并行计算,但是,使用这个框架,你必须指定问题如何被细分(分区),使用聚合操作,Java运行时将为你执行这种分区和组合解决方案。
在使用集合的应用程序中实现并行性的一个困难是集合不是线程安全的,这意味着多个线程不能在不引入线程干扰或内存一致性错误的情况下操作集合,Collections框架提供同步包装器,可以将自动同步添加到任意集合,使其线程安全。但是,同步会引入线程争用,你希望避免线程争用,因为这会阻止线程并行运行。聚合操作和并行流使你能够使用非线程安全的集合实现并行性,前提是在操作集合时不修改集合。
请注意,并行性并不会自动比串行执行操作快,尽管如果你有足够的数据和处理器内核,并行性可以更快。虽然聚合操作使你能够更容易地实现并行性,但是确定应用程序是否适合并行性仍然是你的职责。
你可以在示例ParallelismExamples中找到本节中描述的代码摘录。
并行执行流
你可以串行或并行执行流,当流并行执行时,Java运行时将流划分为多个子流,聚合操作迭代并并行处理这些子流,然后组合结果。
当你创建一个流时,它总是一个串行流,除非另有指定,要创建并行流,请调用操作Collection.parallelStream,或者,调用操作BaseStream.parallel,例如,下面的语句并行计算所有男性成员的平均年龄:
double average = roster
.parallelStream()
.filter(p -> p.getGender() == Person.Sex.MALE)
.mapToInt(Person::getAge)
.average()
.getAsDouble();
并发归纳
再次考虑以下按性别对成员进行分组的示例(在小节归纳部分中进行了描述),这个例子调用了collect操作,它将集合roster归纳为Map:
Map> byGender =
roster
.stream()
.collect(
Collectors.groupingBy(Person::getGender));
下面是等价的并行操作:
ConcurrentMap> byGender =
roster
.parallelStream()
.collect(
Collectors.groupingByConcurrent(Person::getGender));
这称为并发归纳,如果以下所有条件对包含collect操作的特定管道都成立,Java运行时将执行并发归纳:
注意:这个示例返回ConcurrentMap而不是Map的实例,并