我是并行流的新手,并尝试制作1个示例程序,该程序将计算* 100(1到100)的值并将其存储在map中。 在执行代码时,每次迭代的计数都不同。 我在某处可能是错的,所以请任何知道的正确方法指导我。
代码:
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个唯一编号,需要对它们执行一些相同的操作。简而言之,有处理某些内容的功能。
答案 0 :(得分:5)
您对HashMap
和l
变量的访问都是不是线程安全的,这就是为什么每次运行的输出都不同的原因。
要做的正确方法是将Stream
元素收集到Map
中:
Map<Integer, Integer> resultMap =
dataList.parallelStream()
.collect(Collectors.toMap (Function.identity (), Main::makeSomeMagic));
编辑:l
变量仍使用此代码以非线程安全的方式进行更新,因此,如果变量的最终值是必需的,则必须添加自己的线程安全性对你很重要。
答案 1 :(得分:2)
通过在resultMap
中输入一些值,您使用的是side-effect:
dataList.parallelStream().map(f -> {
Integer xx = 0;
{
xx = makeSomeMagic(f);
}
resultMap.put(f, xx);
return 0;
})
API指出:
无状态操作(例如过滤器和映射)不会保留以下状态: 处理新元素时先前看到的元素-每个元素 可以独立于其他元素上的操作进行处理。
继续进行with:
如果以下原因导致流管道结果可能不确定或不正确 流操作的行为参数是有状态的。一种 有状态的lambda(或其他实现了适当的 功能接口)是一个其结果取决于任何状态的接口 在执行流水线期间可能会发生变化。
它遵循与您的示例相似的示例:
...如果并行执行映射操作,则结果为 由于线程调度,相同的输入可能因运行而异 差异,而使用无状态lambda表达式时,结果 永远都是一样的。
这解释了您的观察:每次运行输出都不同。
正确的方法是@Eran的shown
答案 2 :(得分:0)
希望它能正常工作。通过使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);
}
}
答案 3 :(得分:0)
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);
}