• JDK1.8 CopyOnWriteArrayList源码学习


    ArrayList

    底层:Object数组,非线程安全

    默认容量:10,其实是0,第一次add时,才会主动去扩容

    每一扩容,变为原来容量的1.5倍。10->15->22

    /*      */   private void grow(int minCapacity)
    /*      */   {
    /*  254 */     int oldCapacity = elementData.length;
    /*  255 */     int newCapacity = oldCapacity + (oldCapacity >> 1);
    /*  256 */     if (newCapacity - minCapacity < 0)
    /*  257 */       newCapacity = minCapacity;
    /*  258 */     if (newCapacity - 2147483639 > 0) {
    /*  259 */       newCapacity = hugeCapacity(minCapacity);
    /*      */     }
    /*  261 */     elementData = Arrays.copyOf(elementData, newCapacity);
    /*      */   }
    

      

    线程不安全

    非线程安全的case:ConcurrentModificationException

        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            for (int i = 0; i < 30; i++) {
                Thread thread = new Thread(() -> {
                    list.add(UUID.randomUUID().toString());
                    System.out.println(list);
                });
                thread.start();
            }
        }
    

      为什么会报错:遍历ArrayList时,另一线程执行add操作,会造成modCount变化,fail-fast思想

     */     final void checkForComodification() {
    /*  900 */       if (modCount != expectedModCount) {
    /*  901 */         throw new ConcurrentModificationException();
    /*      */       }
    /*      */     }
    

      

    Exception in thread "Thread-25" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at java.util.AbstractCollection.toString(AbstractCollection.java:461)
    at java.lang.String.valueOf(String.java:2994)
    at java.io.PrintStream.println(PrintStream.java:821)
    at com.hashmapt.ArrayListTest.lambda$0(ArrayListTest.java:29)
    at java.lang.Thread.run(Thread.java:745)
    [82b9f5c7-7c17-48a1-87e0-e1ff90e084bb, fd427bbf-6adf-45af-b806-5af9a044d6d7]
    [82b9f5c7-7c17-48a1-87e0-e1ff90e084bb, fd427bbf-6adf-45af-b806-5af9a044d6d7, 96f5bb81-c50b-441b-9833-e81c364b3c5f, c4894981-260b-4b7b-8076-168703f7e8ba]
    [82b9f5c7-7c17-48a1-87e0-e1ff90e084bb, fd427bbf-6adf-45af-b806-5af9a044d6d7, 96f5bb81-c50b-441b-9833-e81c364b3c5f, c4894981-260b-4b7b-8076-168703f7e8ba, c201001b-41a8-4919-ad5f-5e36bd86a56b]
    [82b9f5c7-7c17-48a1-87e0-e1ff90e084bb, fd427bbf-6adf-45af-b806-5af9a044d6d7, 96f5bb81-c50b-441b-9833-e81c364b3c5f]
    [82b9f5c7-7c17-48a1-87e0-e1ff90e084bb, fd427bbf-6adf-45af-b806-5af9a044d6d7, 96f5bb81-c50b-441b-9833-e81c364b3c5f, c4894981-260b-4b7b-8076-168703f7e8ba, c201001b-41a8-4919-ad5f-5e36bd86a56b, a492afac-264b-4111-9876-3e0e01b606db, 48dcca36-b425-451e-ab8b-1ad0967bfa91]
    [82b9f5c7-7c17-48a1-87e0-e1ff90e084bb, fd427bbf-6adf-45af-b806-5af9a044d6d7, 96f5bb81-c50b-441b-9833-e81c364b3c5f, c4894981-260b-4b7b-8076-168703f7e8ba, c201001b-41a8-4919-ad5f-5e36bd86a56b, a492afac-264b-4111-9876-3e0e01b606db, 48dcca36-b425-451e-ab8b-1ad0967bfa91, 65c3cb2c-b4d4-4b59-ad2a-0c2194733d58]
    [82b9f5c7-7c17-48a1-87e0-e1ff90e084bb, fd427bbf-6adf-45af-b806-5af9a044d6d7, 96f5bb81-c50b-441b-9833-e81c364b3c5f, c4894981-260b-4b7b-8076-168703f7e8ba, c201001b-41a8-4919-ad5f-5e36bd86a56b, a492afac-264b-4111-9876-3e0e01b606db, 48dcca36-b425-451e-ab8b-1ad0967bfa91, 65c3cb2c-b4d4-4b59-ad2a-0c2194733d58, 80dca1e1-c3bb-4b70-9cbd-f3ebfb699093]
    [82b9f5c7-7c17-48a1-87e0-e1ff90e084bb, fd427bbf-6adf-45af-b806-5af9a044d6d7]
    [82b9f5c7-7c17-48a1-87e0-e1ff90e084bb, fd427bbf-6adf-45af-b806-5af9a044d6d7, 9

    如何解决ArrayList线程不安全:

    方法1:使用Vector

        public static void main(String[] args) {
    //        List<String> list = new ArrayList<String>();
            List<String> list = new Vector<String>();
            for (int i = 0; i < 30; i++) {
                Thread thread = new Thread(() -> {
                    list.add(UUID.randomUUID().toString());
                    System.out.println(list);
                });
                thread.start();
            }
        }
    

      

    Vector是线程安全的容器,原理是对add,remove,iterator等所有方法都加synchronized锁

    /*      */   public synchronized boolean add(E e)
    /*      */   {
    /*  781 */     modCount += 1;
    /*  782 */     ensureCapacityHelper(elementCount + 1);
    /*  783 */     elementData[(elementCount++)] = e;
    /*  784 */     return true;
    /*      */   }
    

      此容器虽然线程安全,但是效率极低,使用场景很少

    方法2:使用Collections.synchronizedList

        public static void main(String[] args) {
    //        List<String> list = new ArrayList<String>();
    //        List<String> list = new Vector<String>();
            List<String> list = Collections.synchronizedList(new ArrayList<>());
            for (int i = 0; i < 30; i++) {
                Thread thread = new Thread(() -> {
                    list.add(UUID.randomUUID().toString());
                    System.out.println(list);
                });
                thread.start();
            }
        }
    

      其原理同Vector,对所有操作加synchronized锁

    方法三:使用CopyOnWriteArrayList

        public static void main(String[] args) {
    //        List<String> list = new ArrayList<String>();
    //        List<String> list = new Vector<String>();
    //        List<String> list = Collections.synchronizedList(new ArrayList<>());
            List<String> list = new CopyOnWriteArrayList<String>();
            for (int i = 0; i < 30; i++) {
                Thread thread = new Thread(() -> {
                    list.add(UUID.randomUUID().toString());
                    System.out.println(list);
                });
                thread.start();
            }
        }
    

      原理:写时复制,即只对add操作加锁,读操作不加锁。

    add时,拷贝一份副本到内存,扩容后,将array指针指向新的数组

    /*      */   public boolean add(E e)
    /*      */   {
    /*  434 */     ReentrantLock lock = this.lock;
    /*  435 */     lock.lock();
    /*      */     try {
    /*  437 */       Object[] elements = getArray();
    /*  438 */       int len = elements.length;
    /*  439 */       Object[] newElements = Arrays.copyOf(elements, len + 1);
    /*  440 */       newElements[len] = e;
    /*  441 */       setArray(newElements);
    /*  442 */       return true;
    /*      */     } finally {
    /*  444 */       lock.unlock();
    /*      */     }
    /*      */   }
    

      此博客详细介绍了CopyOnWrite的使用场景及优缺点

    聊聊并发-Java中的Copy-On-Write容器

    程序员必备基础知识,CopyOnWrite思想,其实非常简单,源码解析

  • 相关阅读:
    Silverlight 控件绑定到对象
    开源编辑器Makedown的安装
    ASP.NET 4 和 Visual Web Developer 中的新增功能
    空间新闻模块CSS
    P2P之王者电骡Emule技术分析
    QQ向陌生人聊天的autoit脚本
    Er 一个开源游戏的诞生
    一个不错的源代码语法高亮插件dp.SyntaxHighlighter
    Aut2Exe编译au3脚本为可执行文件
    模拟:悬停和纯双点击移动设备的CSS
  • 原文地址:https://www.cnblogs.com/lt123/p/13289146.html
Copyright © 2020-2023  润新知