日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java基础之CopyOnWriteArrayList

發布時間:2025/7/25 java 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java基础之CopyOnWriteArrayList 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

今天我們來學習一下CopyOnWriteArrayList這個比ArrayList更安全的集合,同時帶著以下幾個問題去分析源碼。

  • CopyOnWriteArrayList如何保證線程安全?
  • CopyOnWriteArrayList有什么優缺點嗎?
  • CopyOnWriteArrayList與ArrayList有什么區別嗎?
  • 了解CopyOnWriteArraySet嗎?

繼承關系

public class CopyOnWriteArrayList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable {//......} 復制代碼

首先我們來看一下CopyOnWriteArrayList的繼承關系,從源碼中可以看出它分別實現了List、RandomAccess、Cloneable以及Serializable,對于我們比較熟悉的應該是List和Serializable了。

核心字段

//可以看出這是重入鎖final transient ReentrantLock lock = new ReentrantLock();//底層數組,可以看出用了volatileprivate transient volatile Object[] array; 復制代碼

通過以上兩個核心字段,可以猜出CopyOnWriteArrayList內部進行了加鎖來保證線程安全。那么它到底是如何實現的呢?我們繼續往下分析。

構造方法

/*** 默認構造方法,初始化了一個空數組*/public CopyOnWriteArrayList() {setArray(new Object[0]);} 復制代碼

再來簡單看一下其他兩個構造方法,如下:

public CopyOnWriteArrayList(Collection<? extends E> c) {Object[] elements;if (c.getClass() == CopyOnWriteArrayList.class)elements = ((CopyOnWriteArrayList<?>)c).getArray();else {elements = c.toArray();// c.toArray might (incorrectly) not return Object[] (see 6260652)if (elements.getClass() != Object[].class)elements = Arrays.copyOf(elements, elements.length, Object[].class);}setArray(elements);}public CopyOnWriteArrayList(E[] toCopyIn) {setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));}復制代碼

修改

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;//設置array為newElementssetArray(newElements);} else {// Not quite a no-op; ensures volatile write semanticssetArray(elements);}return oldValue;} finally {//解鎖lock.unlock();}} 復制代碼

可以看出在寫入數據時通過加鎖來保證線程安全,并且通過復制一個新數組。然后修改新數組中index對應的Value值,最后將新數組賦值給CopyOnWriteArrayList的底層數組array,也可以說是將array數組指向新數組,如下圖:

對于CopyOnWriteArrayList的set()方法,我們可以這樣理解。

  • 獲取舊數組
  • 通過復制舊數組創建新數組
  • 在新數組中進行修改
  • array指向新數組

添加

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();}} 復制代碼

可以看出與set()方法類似,也是通過加鎖的方式來保證線程安全。然后創建一個新數組來添加元素,最后將新數組賦值給array。

獲取

public E get(int index) {return get(getArray(), index);} @SuppressWarnings("unchecked") private E get(Object[] a, int index) {return (E) a[index];} 復制代碼

從get()方法源碼中可以看出 ,在從CopyOnWriteArrayList獲取元素時并沒有進行加鎖。那么我們就要思考一個問題了,如果當寫入數據時,在沒有寫完數據的情況下。再去讀取數據,那么我們能準確的獲得數據嗎?而且重點是在get()方法中,是直接對底層數組array進行操作的。那就意味著在我們讀取數據時,有可能獲得的是舊數據。也就是說CopyOnWriteArrayList并不能保證數據的實時性,主要原因可以從以下幾點來思考。

  • 在get()方法并沒有進行加鎖
  • 讀取的是底層數組array的數據

刪除

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();}} 復制代碼

可以看出remove()方法也是一樣的,通過加鎖來保證線程安全。

其他方法

/*** 獲取底層數組*/final Object[] getArray() {return array;}/*** 設置底層數組*/final void setArray(Object[] a) {array = a;}/*** 獲取數組大小*/public int size() {return getArray().length;}/*** 判斷數組是否為空*/ public boolean isEmpty() {return size() == 0;} 復制代碼

優點與缺點

通過查看源碼,我們可以看出CopyOnWriteArrayList最大的優點就是線程安全,也是最突出的優點。它在讀取操作時沒有加鎖,只有當進行寫入操作時才會加鎖。這也體現出了CopyOnWriteArrayList的一個缺點,那就是無法讀取實時數據。因為可能當寫操作還沒有完成時,就進行了讀取操作,那么讀取的將是舊數據。

CopyOnWriteArraySet

接下來我們來簡單了解一下CopyOnWriteArrayList的表兄弟CopyOnWriteArraySet,主要源碼如下。

private final CopyOnWriteArrayList<E> al;public CopyOnWriteArraySet() {al = new CopyOnWriteArrayList<E>();} 復制代碼

通過構造方法就可以看出初始化了一個CopyOnWriteArrayList,也就是說CopyOnWriteArraySet內部是用CopyOnWriteArrayList來實現的,我們再來看一下其他幾個方法。

public boolean add(E e) {return al.addIfAbsent(e);} public boolean remove(Object o) {return al.remove(o);} 復制代碼

可以看出add()和remove()都是通過CopyOnWriteArrayList來實現。

總結

通過分析源碼,我們了解了CopyOnWriteArrayList的內部實現,以及它是如何保證線程安全的,還了解一下CopyOnWriteArraySet。

轉載于:https://juejin.im/post/5cbf2d0fe51d456e396318d2

總結

以上是生活随笔為你收集整理的Java基础之CopyOnWriteArrayList的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。