日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

CopyOnWriteArrayList的源碼分析

作者:MM呦 更新時間: 2022-09-25 編程語言

文章目錄

  • ArrayList和CopyOnWriteArrayList
    • 1.Se()方法
    • 2.add()方法
    • 3.add()添加指定位置的元素
    • 4.Remove()方法
  • 總結


ArrayList和CopyOnWriteArrayList

我們知道多線程并發時ArraysList是線程不安全,而CopyOnWriteArrayList是線程安全,那么CopyOnWriteArrayList是如何保證線程安全的呢?通過以下常用的方法說明:

1.Se()方法

首先兩個set()方法的作用是一樣的都是替換指定位置的值,但CopyOnWriteArrayList中的set()方法實現ReentrantLock加鎖機制,這就是為什么CopyOnWriteArrayList是線程安全。CopyOnWriteArrayList中通過getArray()方法得到原來的數組可理解為oldArray[ ];再通過get(elements, index)方法將原來該下標位置的值取出即為oldValue;然后判斷要修改的值是否與原來的值相等,如果相等則將原數組重新放回,如果不相等則進行元素替換通過Arrays.copyOf(elements, len)方法復制新的數組,將指定位置的值進行修改;最后將修改完的數組放回并釋放鎖

ArrayList的set()方法
	public E set(int index, E element) {
        rangeCheck(index);
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }
CopyOnWriteArrayList的set()方法
	public E set(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            E oldValue = get(elements, index);
            if (oldValue != element) {
                int len = elements.length;
                Object[] newElements = Arrays.copyOf(elements, len);
                newElements[index] = element;
                setArray(newElements);
            } else {
                // Not quite a no-op; ensures volatile write semantics
                setArray(elements);
            }
            return oldValue;
        } finally {
            lock.unlock();
        }
    }    

2.add()方法

同理,CopyOnWriteArrayList中的add()方法是實現ReentrantLock鎖機制也是線程安全。也是通過getArray()得到原來數組;再通過Arrays.copyOf(elements, len + 1)方法復制原來的數組oldArray[ ],但比原數組的長度多1,是因為將要添加的元素采用尾插法存儲至數組尾部;最后依舊將新數組放回并釋放鎖

ArrayList的add()方法
	public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
CopyOnWriteArrayList的add()方法
	public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

3.add()添加指定位置的元素

通過getArray()得到原來數組;先判斷指定的下標是否越界或不規范,如果不符合則拋出下標越界的異常,如果符合;再判斷該元素移動多少個元素,如果發現移動0個元素那么證明要添加的元素應為數組的尾部,那么直接與添加元素的原理相同,如果不等于0那么證明該元素被添加在數組的中間某個位置,則通過System.arraycopy(elements, 0, newElements, 0, index)方法將該下標之前的元素復制出來至新數組、再通過System.arraycopy(elements, index, newElements, index + 1,numMoved)將該下標后半部分元素復制至新數組,再將元素添加至該下標;最后將新數組放回并釋放鎖

ArrayList的add()方法
	 public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }
CopyOnWriteArrayList的add()方法
	public void add(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (index > len || index < 0)
                throw new IndexOutOfBoundsException("Index: "+index+
                                                    ", Size: "+len);
            Object[] newElements;
            int numMoved = len - index;
            if (numMoved == 0)
                newElements = Arrays.copyOf(elements, len + 1);
            else {
                newElements = new Object[len + 1];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index, newElements, index + 1,
                                 numMoved);
            }
            newElements[index] = element;
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

4.Remove()方法

通過getArray()得到原來數組;獲取到指定下標的元素、先判斷刪除的元素是否為數組中最后一個元素,如果是數組中最后一個元素則直接復制len-1個元素至新數組再將新數組放回,如果不是最后一個通過 System.arraycopy(elements, 0, newElements, 0, index)方法將刪除元素下標之前的元素復制至新數組再通過System.arraycopy(elements, index + 1, newElements, index, numMoved)方法將刪除元素下標之后的元素復制至新數組;最后將新數組放回并釋放鎖

ArrayList的remove()
	public E remove(int index) {
        rangeCheck(index);
        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }
CopyOnWriteArrayList的add()方法
	 public E remove(int index) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            E oldValue = get(elements, index);
            int numMoved = len - index - 1;
            if (numMoved == 0)
                setArray(Arrays.copyOf(elements, len - 1));
            else {
                Object[] newElements = new Object[len - 1];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index + 1, newElements, index,
                                 numMoved);
                setArray(newElements);
            }
            return oldValue;
        } finally {
            lock.unlock();
        }
    }

總結

  • 多線程并發時,ArrayList是線程不安全,而CopyOnWriteArrayList是線程安全就是因為CopyOnWriteArrayList實現ReentrantLock鎖機制,也正因為鎖機制CopyOnWriteArrayList的速度沒有ArrayList的速度快。
  • ArrayList是會進行擴容,CopyOnWriteArrayList每次都是復制數組無需擴容,但大量的數組復制也是很消耗內存及cpu的性能。
  • CopyOnWriteArrayList由于只在寫時加鎖,讀時無鎖,導致多線程讀數據時數據并一定是最新的數據,導致數據無法做到實時性,也常用于寫少讀多的場景。
  • 還有一些方法都類似與上面的原理,自己可以找找源代碼再查看。

原文鏈接:https://blog.csdn.net/qq_55135629/article/details/126922390

欄目分類
最近更新