高并发下ArrayList空值(null)问题,arraylistnull
分享于 点击 32973 次 点评:270
高并发下ArrayList空值(null)问题,arraylistnull
对于ArrayList,有一篇blog专门的介绍,它不适用于多线程环境中,废话不多说,直接上代码让大家看个明白!
public class ArrayListTest { @Test public void test() { List<Person> personList = new ArrayList<>(); for (int i = 0; i < 9; i++) { int finalI = i; new Thread(new Runnable() { @Override public void run() { Person person = new Person(); person.setAge(finalI); person.setName("name" + finalI); personList.add(person); } } ).start(); } try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } print(personList); } public void print(List<Person> personList) { System.out.println(personList.size()); personList.forEach(item -> { if (item == null) { System.out.println("值" + item); } }); } }
这个写了一个测试类,开了9个线程往arrarList里面添加值, 其中Person就只有两个属性 age(int),name(string),您能想到打印的结果吗?
有些人说这还不简单呀,
9
就ok了!
但是真的是这样吗?
其实大部分情况下,确实打印的结果是9,有少数几次是
9
值null
为什么会出现值为null的情况呢?
这就要从ArrayList的数据结构和源码分析了。博客写的很清楚,这里就不多啰嗦了。关键点是,ArrayList的add的方法不是线程安全的。还是带大家大致看下源码吧!
public boolean add(E e) { //添加元素 复写了接口List里面的方法,这个方法没有任何的锁,也没有看到cas ensureCapacityInternal(size + 1); // 增加修改记录方法 elementData[size++] = e; return true; }
private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //如果是开始空的数据,这时需要将list的大小设置为10个 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); //这里面就不看了,无非就是扩扩容 }
分析:ArrayList方法添加方法是非线程安全的,添加操作可以简单理解为两个步骤
1.给索引位置赋值
2.size加1
如果现在有两个线程a,b,size为5,索引为4,a线程读到索引位值时,将值赋值到这个位置,这时cpu时间片让出,b线程读到的索引位置也是4,将size加1。这个时候a线程恢复cpu时间片,size加1。这样,索引位置值变成了a线程赋值的值,b线程的值被覆盖了,但是size却加了2次,这样取值的时候自然就取到了空值。
解决办法:
1.尽量避免在多线程的环境中使用ArrayList.
2.使用Collections.synchronizedList 方法包装list,它里面使用的是synchronized关键字来实现线程安全的。
3.CopyOnWriteArrayList(线程安全,基于copy思想,但是数据过多,可能导致gc频繁,需要衡量。)
4.......
相关文章
- 暂无相关文章
用户点评