欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > > 文章正文

高并发下ArrayList空值(null)问题,arraylistnull

来源: javaer 分享于  点击 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.......

相关文章

    暂无相关文章

用户点评