COW機制以及相關類

  • Vector和SynchronizedList

    • 咱們知道ArrayList是用於替代Vector的,Vector是線程安全的容器。由於它幾乎在每一個方法聲明處都加了synchronized關鍵字來使容器安全。
    • 若是使用Collections.synchronizedList(new ArrayList())來使ArrayList變成是線程安全的話,也是幾乎都是每一個方法都加上synchronized關鍵字的,只不過它不是加在方法的聲明處,而是方法的內部
    • 多線程下for循環迭代Vector或者SynchronizedList,進行delete和get操做會發生數組下標錯誤的異常。java

      • 在JDK5之後,Java推薦使用for-each(迭代器)來遍歷咱們的集合,好處就是簡潔、數組索引的邊界值只計算一次
    • 若是使用for-each(迭代器)來作上面的操做,會拋出ConcurrentModificationException異常。
    • 若是想要完美解決上面所講的問題,咱們能夠在遍歷前加鎖數組

      • 遍歷一下容器都要我加上鎖,這這這不是要慢死了嗎。的確是挺慢的。由於加鎖粒度太大。
  • CopyOnWriteArrayList是同步List的替代品,CopyOnWriteArraySet是同步Set的替代品。

    • Hashtable、Vector加鎖的粒度大(直接在方法聲明處使用synchronized)
    • ConcurrentHashMap、CopyOnWriteArrayList加鎖粒度小(用各類的方式來實現線程安全,好比咱們知道的ConcurrentHashMap用了cas鎖、volatile等方式來實現線程安全..)
    • JUC下的線程安全容器在遍歷的時候不會拋出ConcurrentModificationException異常
    • 因此通常來講,咱們都會使用JUC包下給咱們提供的線程安全容器,而不是使用老一代的線程安全容器。
  • CopyOnWriteArrayList實現原理

    • CopyOnWriteArrayList是線程安全容器(相對於ArrayList),底層經過複製數組的方式來實現。
    • CopyOnWriteArrayList在遍歷的使用不會拋出ConcurrentModificationException異常,而且遍歷的時候就不用額外加鎖
    • 元素能夠爲null
    /** 可重入鎖對象 */
        final transient ReentrantLock lock = new ReentrantLock();
        /** CopyOnWriteArrayList底層由數組實現,volatile修飾 */
        private transient volatile Object[] array;
    
        final Object[] getArray() {
            return array;
        }
        final void setArray(Object[] a) {
            array = a;
        }
        // 初始化CopyOnWriteArrayList至關於初始化數組
        public CopyOnWriteArrayList() {
            setArray(new Object[0]);
        }
    • CopyOnWriteArrayList底層就是數組,加鎖就交由ReentrantLock來完成。安全

      • 經過代碼咱們能夠知道:在add(),set(),remove() 的時候就上鎖,並複製一個新數組,增長操做在新數組上完成,將array指向到新數組中,最後解鎖。
      • 在修改時,複製出一個新數組,修改的操做在新數組中完成,最後將新數組交由array變量指向
      • 寫加鎖,讀不加鎖
  • CopyOnWriteArrayList缺點

    • 內存佔用:若是CopyOnWriteArrayList常常要增刪改裏面的數據,常常要執行add()、set()、remove()的話,那是比較耗費內存的。多線程

      • 由於咱們知道每次add()、set()、remove()這些增刪改操做都要複製一個數組出來。
    • 數據一致性:CopyOnWrite容器只能保證數據的最終一致性,不能保證數據的實時一致性線程

      • 從上面的例子也能夠看出來,好比線程A在迭代CopyOnWriteArrayList容器的數據。線程B在線程A迭代的間隙中將CopyOnWriteArrayList部分的數據修改了(已經調用setArray()了)。可是線程A迭代出來的是原有的數據。
相關文章
相關標籤/搜索