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 list = new ArrayList();

  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 list;

  34. private CountDownLatch countDownLatch;

  35.  
  36. public MyThread(List 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. }

  50. 代码转载: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();

    你可能感兴趣的:(JAVA)