[英]java 8 parallel stream confusion/issue
我是并行流的新手,并试图制作一个计算值* 100(1到100)并将其存储在地图中的示例程序。 在执行代码时,每次迭代都会有不同的数量。 我可能在某个地方错了所以请指导我,任何人都知道正确的方法。
代码 :
import java.util.*;
import java.lang.*;
import java.io.*;
import java.util.stream.Collectors;
public class Main{
static int l = 0;
public static void main (String[] args) throws java.lang.Exception {
letsGoParallel();
}
public static int makeSomeMagic(int data) {
l++;
return data * 100;
}
public static void letsGoParallel() {
List<Integer> dataList = new ArrayList<>();
for(int i = 1; i <= 100 ; i++) {
dataList.add(i);
}
Map<Integer, Integer> resultMap = new HashMap<>();
dataList.parallelStream().map(f -> {
Integer xx = 0;
{
xx = makeSomeMagic(f);
}
resultMap.put(f, xx);
return 0;
}).collect(Collectors.toList());
System.out.println("Input Size: " + dataList.size());
System.out.println("Size: " + resultMap.size());
System.out.println("Function Called: " + l);
}
}
最后输出
输入大小:100
尺寸:100
功能称为:98
每次运行输出都不同。 我想在我自己的应用程序中使用并行流,但由于这种混淆/问题我不能。 在我的应用程序中,我有100-200个唯一编号,需要执行相同的操作。 简而言之,它有处理某些东西的功能。
您对HashMap
和l
变量的访问都不是线程安全的,这就是每次运行时输出不同的原因。
执行您要执行的操作的正确方法是将Stream
元素收集到Map
:
Map<Integer, Integer> resultMap =
dataList.parallelStream()
.collect(Collectors.toMap (Function.identity (), Main::makeSomeMagic));
编辑:使用此代码仍然以非线程安全的方式更新l
变量,因此如果变量的最终值对您很重要,则必须添加自己的线程安全性。
通过在resultMap
放置一些值,您将使用副作用 :
dataList.parallelStream().map(f -> {
Integer xx = 0;
{
xx = makeSomeMagic(f);
}
resultMap.put(f, xx);
return 0;
})
API声明:
无状态操作(例如过滤器和映射)在处理新元素时不保留先前看到的元素的状态 - 每个元素都可以独立于其他元素上的操作进行处理。
事情有 :
如果流操作的行为参数是有状态的,则流管道结果可能是不确定的或不正确的。 有状态lambda(或实现适当功能接口的其他对象)的结果取决于在流管道执行期间可能发生变化的任何状态。
它遵循一个类似于你的例子显示:
...如果映射操作是并行执行的,由于线程调度的差异,相同输入的结果可能因运行而异,而对于无状态lambda表达式,结果将始终相同。
这解释了你的观察: 每次运行输出都不同。
@Eran 显示了正确的方法
希望它工作正常。 通过制作Synchronied
函数makeSomeMagic
并使用Threadsafe数据结构ConcurrentHashMap
并编写简单语句
dataList.parallelStream().forEach(f -> resultMap.put(f, makeSomeMagic(f)));
整个代码在这里:
import java.util.*;
import java.lang.*;
import java.io.*;
import java.util.stream.Collectors;
public class Main{
static int l = 0;
public static void main (String[] args) throws java.lang.Exception {
letsGoParallel();
}
public synchronized static int makeSomeMagic( int data) { // make it synchonized
l++;
return data * 100;
}
public static void letsGoParallel() {
List<Integer> dataList = new ArrayList<>();
for(int i = 1; i <= 100 ; i++) {
dataList.add(i);
}
Map<Integer, Integer> resultMap = new ConcurrentHashMap<>();// use ConcurrentHashMap
dataList.parallelStream().forEach(f -> resultMap.put(f, makeSomeMagic(f)));
System.out.println("Input Size: " + dataList.size());
System.out.println("Size: " + resultMap.size());
System.out.println("Function Called: " + l);
}
}
Stream
将帮助您循环使用字节代码。 Stream
,不要在多线程中使用没有线程安全的变量(包括parallelStream
) 像这样。
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class ParallelStreamClient {
// static int l = 0;---> no need to count times.
public static void main(String[] args) throws java.lang.Exception {
letsGoParallel();
}
public static int makeSomeMagic(int data) {
// l++;-----> this is no thread-safe way
return data * 100;
}
public static void letsGoParallel() {
List<Integer> dataList = new ArrayList<>();
for (int i = 1; i <= 100; i++) {
dataList.add(i);
}
Map<Integer, Integer> resultMap =
dataList.parallelStream().collect(Collectors.toMap(i -> i,ParallelStreamClient::makeSomeMagic));
System.out.println("Input Size: " + dataList.size());
System.out.println("Size: " + resultMap.size());
//System.out.println("Function Called: " + l);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.