1、前言
Java8的stream接口极大地减少了for循环写法的复杂性,stream提供了map/reduce/collect等一系列聚合接口,还支持并发操作:parallelStream。
Java8的paralleStream用fork/join框架提供了并发执行能力。但是如果使用不当,很容易陷入误区。
2、parallelStream并发流线程安全问题
@Test
public void test3() {
List<Integer> param = Lists.newArrayList();
for (int i = 0; i < 100; i++) {
param.add(i);
}
log.info("for循环的入参长度为:{}", param.size());
for (int i = 0; i < 50; i++) {
List<Integer> resultList = Lists.newArrayList();
Map<Integer, Integer> resultMap = Maps.newHashMap();
param.parallelStream()
.forEach(v -> {
resultList.add(v);
resultMap.put(v, v);
});
System.out.println("i="+i+"的结果数据为:resultList长度="+resultList.size()+";resultMap长度="+resultMap.size());
}
}
3、输出结果
2023-01-17 17:03:41.372 INFO 8552 --- [ main] c.q.r.p.management.clander.CalendarTest : for循环的入参长度为:100
i=0的结果数据为:resultList长度=98;resultMap长度=99
i=1的结果数据为:resultList长度=100;resultMap长度=99
i=2的结果数据为:resultList长度=100;resultMap长度=99
i=3的结果数据为:resultList长度=99;resultMap长度=99
i=4的结果数据为:resultList长度=99;resultMap长度=98
i=5的结果数据为:resultList长度=99;resultMap长度=96
i=6的结果数据为:resultList长度=100;resultMap长度=99
i=7的结果数据为:resultList长度=100;resultMap长度=100
i=8的结果数据为:resultList长度=100;resultMap长度=100
i=9的结果数据为:resultList长度=99;resultMap长度=100
i=10的结果数据为:resultList长度=100;resultMap长度=97
i=11的结果数据为:resultList长度=100;resultMap长度=100
i=12的结果数据为:resultList长度=98;resultMap长度=98
i=13的结果数据为:resultList长度=97;resultMap长度=98
i=14的结果数据为:resultList长度=99;resultMap长度=98
i=15的结果数据为:resultList长度=99;resultMap长度=97
i=16的结果数据为:resultList长度=100;resultMap长度=100
i=17的结果数据为:resultList长度=100;resultMap长度=100
i=18的结果数据为:resultList长度=97;resultMap长度=97
i=19的结果数据为:resultList长度=100;resultMap长度=100
i=20的结果数据为:resultList长度=98;resultMap长度=99
i=21的结果数据为:resultList长度=100;resultMap长度=100
i=22的结果数据为:resultList长度=99;resultMap长度=100
i=23的结果数据为:resultList长度=99;resultMap长度=97
i=24的结果数据为:resultList长度=96;resultMap长度=97
i=25的结果数据为:resultList长度=100;resultMap长度=100
i=26的结果数据为:resultList长度=100;resultMap长度=99
i=27的结果数据为:resultList长度=99;resultMap长度=99
i=28的结果数据为:resultList长度=100;resultMap长度=100
i=29的结果数据为:resultList长度=99;resultMap长度=99
i=30的结果数据为:resultList长度=98;resultMap长度=97
i=31的结果数据为:resultList长度=99;resultMap长度=98
i=32的结果数据为:resultList长度=99;resultMap长度=99
i=33的结果数据为:resultList长度=100;resultMap长度=98
i=34的结果数据为:resultList长度=100;resultMap长度=99
i=35的结果数据为:resultList长度=100;resultMap长度=100
i=36的结果数据为:resultList长度=98;resultMap长度=100
i=37的结果数据为:resultList长度=98;resultMap长度=98
i=38的结果数据为:resultList长度=99;resultMap长度=99
i=39的结果数据为:resultList长度=99;resultMap长度=98
i=40的结果数据为:resultList长度=96;resultMap长度=97
i=41的结果数据为:resultList长度=99;resultMap长度=99
i=42的结果数据为:resultList长度=98;resultMap长度=100
i=43的结果数据为:resultList长度=100;resultMap长度=99
i=44的结果数据为:resultList长度=100;resultMap长度=100
i=45的结果数据为:resultList长度=99;resultMap长度=99
i=46的结果数据为:resultList长度=99;resultMap长度=97
i=47的结果数据为:resultList长度=96;resultMap长度=99
i=48的结果数据为:resultList长度=99;resultMap长度=99
i=49的结果数据为:resultList长度=97;resultMap长度=100
4、结论
由输出结果可知,在使用parallelStream流的使用操作线程不安全的ArrayList和HashMap很容易造成数据丢失
5、解决方案
@Test
public void test5() {
List<Integer> param = Lists.newArrayList();
for (int i = 0; i < 100; i++) {
param.add(i);
}
log.info("for循环的入参长度为:{}", param.size());
for (int i = 0; i < 50; i++) {
List<Integer> resultList = Lists.newArrayList();
Map<Integer, Integer> resultMap = Maps.newHashMap();
param.parallelStream().collect(Collectors.toList()).forEach(v->{
resultList.add(v);
resultMap.put(v, v);
});
System.out.println("i="+i+"的结果数据为:resultList长度="+resultList.size()+";resultMap长度="+resultMap.size());
}
}
@Test
public void test4(){
List<Integer> param = Lists.newArrayList();
for (int i = 0; i < 100; i++) {
param.add(i);
}
log.info("for循环的入参长度为:{}", param.size());
for (int i = 0; i < 50; i++) {
//注意此处,不过不建议这样使用,加锁的话会造成性能降低,实际可以自行测试
Vector<Integer> resultList = new Vector();
Map<Integer, Integer> resultMap = Collections.synchronizedMap(Maps.newHashMap());
param.parallelStream()
.forEach(v -> {
resultList.add(v);
resultMap.put(v, v);
});
System.out.println("i="+i+"的结果数据为:resultList长度="+resultList.size()+";resultMap长度="+resultMap.size());
}
}
输出结果[性能自行测试]
2023-01-17 17:19:31.518 INFO 19220 --- [ main] c.q.r.p.management.clander.CalendarTest : for循环的入参长度为:100
i=0的结果数据为:resultList长度=100;resultMap长度=100
i=1的结果数据为:resultList长度=100;resultMap长度=100
i=2的结果数据为:resultList长度=100;resultMap长度=100
i=3的结果数据为:resultList长度=100;resultMap长度=100
i=4的结果数据为:resultList长度=100;resultMap长度=100
i=5的结果数据为:resultList长度=100;resultMap长度=100
i=6的结果数据为:resultList长度=100;resultMap长度=100
i=7的结果数据为:resultList长度=100;resultMap长度=100
i=8的结果数据为:resultList长度=100;resultMap长度=100
i=9的结果数据为:resultList长度=100;resultMap长度=100
i=10的结果数据为:resultList长度=100;resultMap长度=100
i=11的结果数据为:resultList长度=100;resultMap长度=100
i=12的结果数据为:resultList长度=100;resultMap长度=100
i=13的结果数据为:resultList长度=100;resultMap长度=100
i=14的结果数据为:resultList长度=100;resultMap长度=100
i=15的结果数据为:resultList长度=100;resultMap长度=100
i=16的结果数据为:resultList长度=100;resultMap长度=100
i=17的结果数据为:resultList长度=100;resultMap长度=100
i=18的结果数据为:resultList长度=100;resultMap长度=100
i=19的结果数据为:resultList长度=100;resultMap长度=100
i=20的结果数据为:resultList长度=100;resultMap长度=100
i=21的结果数据为:resultList长度=100;resultMap长度=100
i=22的结果数据为:resultList长度=100;resultMap长度=100
i=23的结果数据为:resultList长度=100;resultMap长度=100
i=24的结果数据为:resultList长度=100;resultMap长度=100
i=25的结果数据为:resultList长度=100;resultMap长度=100
i=26的结果数据为:resultList长度=100;resultMap长度=100
i=27的结果数据为:resultList长度=100;resultMap长度=100
i=28的结果数据为:resultList长度=100;resultMap长度=100
i=29的结果数据为:resultList长度=100;resultMap长度=100
i=30的结果数据为:resultList长度=100;resultMap长度=100
i=31的结果数据为:resultList长度=100;resultMap长度=100
i=32的结果数据为:resultList长度=100;resultMap长度=100
i=33的结果数据为:resultList长度=100;resultMap长度=100
i=34的结果数据为:resultList长度=100;resultMap长度=100
i=35的结果数据为:resultList长度=100;resultMap长度=100
i=36的结果数据为:resultList长度=100;resultMap长度=100
i=37的结果数据为:resultList长度=100;resultMap长度=100
i=38的结果数据为:resultList长度=100;resultMap长度=100
i=39的结果数据为:resultList长度=100;resultMap长度=100
i=40的结果数据为:resultList长度=100;resultMap长度=100
i=41的结果数据为:resultList长度=100;resultMap长度=100
i=42的结果数据为:resultList长度=100;resultMap长度=100
i=43的结果数据为:resultList长度=100;resultMap长度=100
i=44的结果数据为:resultList长度=100;resultMap长度=100
i=45的结果数据为:resultList长度=100;resultMap长度=100
i=46的结果数据为:resultList长度=100;resultMap长度=100
i=47的结果数据为:resultList长度=100;resultMap长度=100
i=48的结果数据为:resultList长度=100;resultMap长度=100
i=49的结果数据为:resultList长度=100;resultMap长度=100