ArrayList 为什么说是线程不安全的
这就要从ArrayList的数据结构和源码分析了。关键点是,ArrayList的add的方法不是线程安全的。还是带大家大致看下源码吧!
//添加元素 复写了接口List里面的方法,这个方法没有任何的锁,也没有看到cas
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!! // 增加修改记录方法
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//如果是开始空的数据,这时需要将list的大小设置为10个
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
//这里面就不看了,无非就是扩扩容
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
分析
ArrayList方法添加方法是非线程安全的,添加操作可以简单理解为两个步骤
- 给索引位置赋值
- size加1如果现在有两个线程a,b,size为5,索引为4,a线程读到索引位值时,将值赋值到这个位置,这时cpu时间片让出,b线程读到的索引位置也是4,将size加1。这个时候a线程恢复cpu时间片,size加1。这样,索引位置值变成了a线程赋值的值,b线程的值被覆盖了,但是size却加了2次,这样取值的时候自然就取到了空值。
这样的话大家Git到 为啥说ArrayList 是线程不安全的了嘛?
提供测试代码
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);
}
});
}
}