ArrayList为什么是线程不安全的

ArrayList为什么是线程不安全的

2018年01月19日 19:05:23 Tonny__ 阅读数:1536

 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zc375039901/article/details/79109930

模拟测试给list加入10000条数据,代码:

 

 
  1. public class UnsafeList {

  2. public static void main(String[] args) {

  3. // 进行 10次测试

  4. for (int i = 0; i < 10; i++) {

  5. test();

  6. }

  7. }

  8.  
  9. public static void test() {

  10. // 用来测试的List

  11. List<Object> list = new ArrayList<Object>();

  12. // 线程数量(100)

  13. int threadCount = 100;

  14. // 用来让主线程等待threadCount个子线程执行完毕

  15. CountDownLatch countDownLatch = new CountDownLatch(threadCount);

  16. // 启动threadCount个子线程

  17. for (int i = 0; i < threadCount; i++) {

  18. Thread thread = new Thread(new MyThread(list, countDownLatch));

  19. thread.start();

  20. }

  21. try {

  22. // 主线程等待所有子线程执行完成,再向下执行

  23. countDownLatch.await();

  24. } catch (InterruptedException e) {

  25. e.printStackTrace();

  26. }

  27. // List 的size

  28. System.out.println(list.size());

  29. }

  30. }

  31.  
  32. class MyThread implements Runnable {

  33. private List<Object> list;

  34. private CountDownLatch countDownLatch;

  35.  
  36. public MyThread(List<Object> list, CountDownLatch countDownLatch) {

  37. this.list = list;

  38. this.countDownLatch = countDownLatch;

  39. }

  40.  
  41. public void run() {

  42. // 每个线程向List中添加100个元素

  43. for (int i = 0; i < 1000; i++) {

  44. list.add(new Object());

  45. }

  46. // 完成一个子线程(主线程等待子线程执行完了再执行)

  47. countDownLatch.countDown();

  48. }

  49. }

代码转载:https://www.cnblogs.com/WuXuanKun/p/5556999.html


打印结果:

 

100000
100000
99847
100000
99670
99442
99998
100000
99271
99926

由此可见是ArrayList做add操作时候,会丢失一些数据,所以所Array是线程不安全的。

那么为什么导致漏掉一些数据呢?

来看看ArrayList.add方法

 

 
  1. // Object[] elementData:ArrayList的数据结构是数组类型,list存放的数据就是存放在elementData里面的

  2. // 第1步

  3. public boolean add(E e) {

  4. ensureCapacityInternal(size + 1); // list的size+1

  5. elementData[size++] = e; // 将数据放到数组最后一个

  6. return true;

  7. }

  8.  
  9.  
  10. // 第2步,元素有变化,那么就调用ensureExplicitCapacity方法

  11. private void ensureCapacityInternal(int minCapacity) {

  12. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

  13. minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);

  14. }

  15.  
  16. // 进入ensureExplicitCapacity方法

  17. ensureExplicitCapacity(minCapacity);

  18. }

  19.  
  20.  
  21. // 第3步,元素有变化,那么就调用grow方法

  22. private void ensureExplicitCapacity(int minCapacity) {

  23. modCount++;

  24. // elementData:list的数组元素

  25. // minCapacity: add操作后的容量

  26. if (minCapacity - elementData.length > 0)

  27. grow(minCapacity);

  28. }

  29.  
  30.  
  31. // 第4步

  32. private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; // 为什么要-8,是因为有些虚拟机有一些hear的key

  33. private void grow(int minCapacity) {

  34.  
  35. // 原始list的容量(容量不是list.size)

  36. int oldCapacity = elementData.length;

  37.  
  38. //现在list的容量,此时是做讲原始容量扩大0.5倍,oldCapacity >> 1:2进制右位移,就是除以2的意思

  39. int newCapacity = oldCapacity + (oldCapacity >> 1);

  40. if (newCapacity - minCapacity < 0)

  41. newCapacity = minCapacity;

  42. // 一般不会进入hugeCapacity这个方法,

  43. if (newCapacity - MAX_ARRAY_SIZE > 0)

  44. newCapacity = hugeCapacity(minCapacity);

  45.  
  46. // 复制elementData返回一个新的数组对象,这个时候list.add完成

  47. elementData = Arrays.copyOf(elementData, newCapacity);

  48. }

 

分析为什么会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();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值