parallelStream使用的一个小陷阱

1:前言

小威哥被这场疫情困在家已经好久了,既然困了那么久也不能落下学习啊,遂把前几天遇到的一个很低级的错误分享出来

2:线上问题

前几天在远程办公的时候同事说我的接口报错了,于是我就去看看,报的错误的代码是这样子的

        List<String> list1 = new ArrayList<>();
        List<String> list2 = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            list1.add(String.valueOf(i));
        }
        list1.parallelStream().forEach(p -> {
            list2.add(p);
        });
复制代码

运行上面的代码的话会报ArrayIndexOutOfBoundsException的错误。

这里其实看过我前面一篇介绍stream流式使用技巧的朋友马上就能看出错误所在了。
文章链接:java8的函数式编程和stream使用心得

其实就是很简单的触发了list的越界错误

3:Arraylist

那么准确来说这不是parallelStream发生了错误,而是因为parallelStream在大于等于2个任务的时候,等于是一个多任务划分而形成的多线程操作。这边的错误是使用了线程不安全的Arraylist导致的错误。

那么ArrayList线程不安全的原因其实也非常简单,arraylist分配好数组后容量不够了会进行扩容。那么我们来假设一种场景。

  • 此时数组容量为9,A,B两个线程同时进入校验是否需要扩容阶段
  • 都通过校验不需要扩容
  • A此时写到10的位置,index+1
  • B此时就去写11这个位置,自然数组只有10个位置,B线程就越界了

4:总结

总结起来,这实际上是一个非常基础的错误,但是因为paramllStream的语法糖问题,导致了忽略了这个问题,如果是手动去创造多线程可能一下就会想起来会不会要做一个并发的控制,但是利用这种高效的语法糖反而写的太快了,一时间忘记了。
所以有些时候不见得语法越简练越高级就越好,相反我觉得java在今天能发展成今天这样的成就,也恰恰得益于其繁琐复杂的编程模式和约束力,不至于让程序员们放飞自我。

猜你喜欢

转载自juejin.im/post/5e4544c76fb9a07c9854e338
今日推荐