记一次parallelStream错误使用导致的NullPointerException
parallelStream
在 Java8 中,新增了一个很有用的功能就是 流
,这个功能可以使我们可以快速的写出优雅的代码,其中 stream
是一个串行流(说法可能有误…就是不会采取多线程来进行处理)。还有一种就是 parallelStream
采用 ForkJoinPool
来实现并发,加快执行效率。
所以在使用 parallelStream
的时候一定要注意线程安全的问题,首先看一段代码:
在这段代码中,是首先判断 dataListList
里面对象的 name
属性是否是偶数,是的话则添加至偶数List,反之则添加至奇数List。
然后开始测试这段代码:
1 | public static void main(String[] args) { |
运行一段时间你就会发现,会出现 NullPointerException
,这是因为在之前的 forEach
里面 ArrayList 是一个非线程安全的集合,而 parallelStream
是一个多线程的流,所以就会导致 ArrayList
在并发插入的时候,会出现部分元素是null的情况。具体原因如下:
ArrayList并发不安全
ArrayList并发不安全的点在于 add
方法里面没有使用锁来保证线程安全,下面是 add 的代码:
1 | public boolean add(E e) { |
假设不慎将 ArrayList
用于并发的环境,那么当第一个线程读取到 size 是 0,第二个线程也读取到该 size 也是0,然后都执行完了 ensureCapacityInternal
方法,此时线程一执行完 size++
后被挂起,然后线程二也执行了 size++
,那么此时无论哪一个线程先执行数组的赋值操作,它的值一定会被另一个线程所覆盖。
回到上面并发流空指针那一个例子
出现异常情况的 List
如下:
然后在 Collectors.toMap()
方法里面最终会调用到 HashMap 的 merge 方法,而 HashMap 的 merge 方法第一行就是判断 value
是否为空,所以就导致了 NullPointerException
总结
在使用 parallelStream
一定需要注意并发的安全,同时注意在重构代码的时候如果是这种并发流的话,一定要注意线程安全。
记一次parallelStream错误使用导致的NullPointerException
https://somersames.github.io/2020/05/11/记一次parallelStream错误使用导致的NullPointerException/