执行Java parallel stream时list.size为何变化?相关疑问求证
关于Java并行Stream中list.size()变化的疑问解答
先看你给出的代码:
static void statefullParallelLambdaSet() { Set<Integer> s = new HashSet<>( Arrays.asList(1, 2, 3, 4, 5, 6) ); List<Integer> list = new ArrayList<>(); int sum = s.parallelStream().mapToInt(e -> { // pipeline start if (list.size() <= 3) { // list.size() changes while the pipeline operation is executing. list.add(e); // mapToInt's lambda expression depends on this value, so it's stateful. return e; } else return 0; }).sum(); // terminal operation System.out.println(sum); }
下面逐个解答你的疑问:
1. 为什么pipeline执行期间list.size()会变化?
代码里的lambda逻辑会直接修改共享的list:只要当前list.size() <=3,就执行list.add(e)。从Stream启动(调用parallelStream())到终端操作sum()执行完成的整个过程中,lambda会不断处理Set中的元素,只要满足条件就会给list添加元素,所以list.size()会从0开始动态增长,直到达到3后不再变化——整个pipeline执行过程中list的大小不是固定的,这就是注释想表达的意思。
2. 并行流中多线程同时执行list.add(e),是不是每次执行时list.size()都会变化?
这个说法不准确,而且这里存在严重的线程安全问题:
ArrayList本身不是线程安全的集合,多线程同时调用add()时,会出现并发修改异常、size计算错误(比如多个线程同时读取到size=2,都执行add,最终size可能只变成3而不是4)、元素覆盖等问题。- 就算不考虑线程安全问题,也不是每次执行lambda时size都会“正确”变化:多个线程可能同时读取到同一个size值,导致多次add操作只让size增加一次,所以实际size的变化是不可预测的,和代码逻辑预期的“每次add就+1”不一致。
3. 串行流时list.size()也变化,是不是因为Set无序导致的?
这个理解有误:
- 串行流中
list.size()变化是必然的——lambda逻辑本身会在size<=3时添加元素,所以size一定会从0逐步增加到3,之后不再变化,这个变化和Set是否无序无关。 - Set无序影响的是哪些元素被添加到list中,进而影响最终的sum结果。比如第一次串行执行时可能取到1、2、3先被add,sum是6;第二次可能取到2、4、5先被add,sum是11。但不管元素顺序如何,list的size都会从0增长到3,这个变化过程是确定的,只是添加的元素不同而已。
内容的提问来源于stack exchange,提问作者Powerful_Coder
相关产品推荐
相关产品推荐

