我听说Java 8提供了很多关于并发计算的实用程序。因此,我想知道并行化给定 for 循环的最简单方法是什么?
public static void main(String[] args)
{
Set<Server> servers = getServers();
Map<String, String> serverData = new ConcurrentHashMap<>();
for (Server server : servers)
{
String serverId = server.getIdentifier();
String data = server.fetchData();
serverData.put(serverId, data);
}
}
阅读流媒体,它们都是新的风潮。
特别注意关于并行性的一点:
"使用显式 for 循环处理元素本质上是串行的。流通过将计算重构为聚合操作的管道而不是每个单独元素上的命令式操作来促进并行执行。所有流操作都可以串行或并行执行。
所以回顾一下,没有并行的for循环,它们本质上是串行的。但是,流可以完成这项工作。请看下面的代码:
Set<Server> servers = getServers();
Map<String, String> serverData = new ConcurrentHashMap<>();
servers.parallelStream().forEach((server) -> {
serverData.put(server.getIdentifier(), server.fetchData());
});
这将使用Stream
:
servers.parallelStream().forEach(server -> {
serverData.put(server.getIdentifier(), server.fetchData());
});
我怀疑Collector
在这里可以发挥更大的作用,因为您使用并发集合。
更优雅或功能化的解决方案是仅使用 Collectors toMap 或 toConcurrentMap 函数,这避免了为 ConcurrentHashMap 维护另一个有状态变量,如以下示例所示:
final Set<Server> servers = getServers();
Map<String, String> serverData = servers.parallelStream().collect(
toConcurrentMap(Server::getIdentifier, Server::fetchData));
注意: 1.那些功能接口(Server::getIdentifier or Server::fetchData
(不允许在这里抛出检查异常, 2. 为了获得并行流的全部好处,服务器的数量会很大,并且不涉及I/O,这些功能中纯粹的数据处理(getIdentifier, fetchData
(
请参考 Collectors javadoc at http://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#toConcurrentMap
粘贴的简单示例(上面的例子使用类Server
,这是一个由OP编写的自定义类(:
import java.io.Console;
import java.util.ArrayList;
ArrayList<String> list = new ArrayList<>();
list.add("Item1");
list.add("Item2");
list.parallelStream().forEach((o) -> {
System.out.print(o);
});
控制台输出。顺序可能会有所不同,因为所有内容都并行执行:
Item1
Item2
.parallelStream()
方法于Java v8
年引入。此示例已使用 JDK v1.8.0_181
进行了测试。