ArrayList为什么是线程不安全的,arraylist线程
分享于 点击 22563 次 点评:90
ArrayList为什么是线程不安全的,arraylist线程
模拟测试给list加入10000条数据,代码:
public class UnsafeList {
public static void main(String[] args) {
// 进行 10次测试
for (int i = 0; i < 10; i++) {
test();
}
}
public static void test() {
// 用来测试的List
List<Object> list = new ArrayList<Object>();
// 线程数量(100)
int threadCount = 100;
// 用来让主线程等待threadCount个子线程执行完毕
CountDownLatch countDownLatch = new CountDownLatch(threadCount);
// 启动threadCount个子线程
for (int i = 0; i < threadCount; i++) {
Thread thread = new Thread(new MyThread(list, countDownLatch));
thread.start();
}
try {
// 主线程等待所有子线程执行完成,再向下执行
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
// List 的size
System.out.println(list.size());
}
}
class MyThread implements Runnable {
private List<Object> list;
private CountDownLatch countDownLatch;
public MyThread(List<Object> list, CountDownLatch countDownLatch) {
this.list = list;
this.countDownLatch = countDownLatch;
}
public void run() {
// 每个线程向List中添加100个元素
for (int i = 0; i < 1000; i++) {
list.add(new Object());
}
// 完成一个子线程(主线程等待子线程执行完了再执行)
countDownLatch.countDown();
}
}
代码转载:https://www.cnblogs.com/WuXuanKun/p/5556999.html
打印结果:
100000
100000
99847
100000
99670
99442
99998
100000
99271
99926
由此可见是ArrayList做add操作时候,会丢失一些数据,所以所Array是线程不安全的。
那么为什么导致漏掉一些数据呢?
来看看ArrayList.add方法
// Object[] elementData:ArrayList的数据结构是数组类型,list存放的数据就是存放在elementData里面的
// 第1步
public boolean add(E e) {
ensureCapacityInternal(size + 1); // list的size+1
elementData[size++] = e; // 将数据放到数组最后一个
return true;
}
// 第2步,元素有变化,那么就调用ensureExplicitCapacity方法
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 进入ensureExplicitCapacity方法
ensureExplicitCapacity(minCapacity);
}
// 第3步,元素有变化,那么就调用grow方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// elementData:list的数组元素
// minCapacity: add操作后的容量
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// 第4步
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; // 为什么要-8,是因为有些虚拟机有一些hear的key
private void grow(int minCapacity) {
// 原始list的容量(容量不是list.size)
int oldCapacity = elementData.length;
//现在list的容量,此时是做讲原始容量扩大0.5倍,oldCapacity >> 1:2进制右位移,就是除以2的意思
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 一般不会进入hugeCapacity这个方法,
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 复制elementData返回一个新的数组对象,这个时候list.add完成
elementData = Arrays.copyOf(elementData, newCapacity);
}
分析为什么会add丢失呢?
List对象,做add时,第1步到第3步,都不会改变elementData对象,只有在第4步Arrays.copyOf的时候,返回一个新的数组对象因此:当有线程t1、t2同时进入grow方法,两个线程都会执行Arrays.copyOf方法,返回2个不同的elementData对象,
假如,t1先返回,t2后返回,那么List.elementData == t1.elementData,
然后t2也返回后,这时List.elementData == t2.elementData
这时,t2.elementData就把t1.elementData数据给覆盖了。导致t1.elementData被丢失
这就是ArrayList为什么线程不安全的原因
java面试也会问到这些问题,
1、ArrayList是不是线程不安全的?不是
2、ArrayList为什么是线程不安全的?
3、ArrayList扩容原理?每次扩容是原来size的0.5倍
4、Arrays.copyOf返回的是原始对象、还是新对象?新对象
5、如果让ArrayList变成线程安全的?
List list1 = Collections.synchronizedList(new ArrayList());
或者用List list1 = new Vector();
相关文章
- 暂无相关文章
用户点评