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

歡迎訪問 生活随笔!

生活随笔

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

java

Java - Java集合中的安全失败Fail Safe机制 (CopyOnWriteArrayList)

發布時間:2025/3/21 java 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java - Java集合中的安全失败Fail Safe机制 (CopyOnWriteArrayList) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • Pre
  • 概述
  • fail-safe的容器—CopyOnWriteArrayList
    • add
    • remove函數
  • 例子
  • 缺陷
  • 使用場景

Pre

Java - Java集合中的快速失敗Fail Fast 機制

概述

ArrayList使用fail-fast機制自然是因為它增強了數據的安全性。

但在某些場景,我們可能想避免fail-fast機制拋出的異常,這時我們就要將ArrayList替換為使用fail-safe機制的CopyOnWriteArrayList.

采用安全失敗機制的集合容器,在 Iterator 的實現上沒有設計拋出 ConcurrentModificationException 的代碼段,從而避免了fail-fast。


fail-safe的容器—CopyOnWriteArrayList

寫時復制: 當我們往一個容器添加元素的時候,先將當前容器復制出一個新的容器,然后新的容器里添加元素,添加完元素之后,再將原容器的引用指向新的容器。

好處是我們可以對CopyOnWrite容器進行并發的讀,而不需要加鎖,因為當前容器不會添加任何元素。所以CopyOnWrite容器也是一種讀寫分離的思想,讀和寫不同的容器。


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);// 存放元素enewElements[len] = e;// 設置數組setArray(newElements);return true;} finally {// 釋放鎖lock.unlock();} }

此函數用于將指定元素添加到此列表的尾部,處理流程如下

  • 獲取鎖(保證多線程的安全訪問),獲取當前的Object數組,獲取Object數組的長度為length,進入步驟②。
  • 根據Object數組復制一個長度為length+1的Object數組為newElements(此時,newElements[length]為null),進入下一步驟。
  • 將下標為length的數組元素newElements[length]設置為元素e,再設置當前Object[]為newElements,釋放鎖,返回。這樣就完成了元素的添加。

remove函數

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) // 移動個數為0// 復制后設置數組setArray(Arrays.copyOf(elements, len - 1));else { // 移動個數不為0// 新生數組Object[] newElements = new Object[len - 1];// 復制index索引之前的元素System.arraycopy(elements, 0, newElements, 0, index);// 復制index索引之后的元素System.arraycopy(elements, index + 1, newElements, index,numMoved);// 設置索引setArray(newElements);}// 返回舊值return oldValue;} finally {// 釋放鎖lock.unlock();} }
  • ①獲取鎖,獲取數組elements,數組長度為length,獲取索引的值elements[index],計算需要移動的元素個數(length - index - 1),若個數為0,則表示移除的是數組的最后一個元素,復制elements數組,復制長度為length-1,然后設置數組,進入步驟③;否則,進入步驟②

  • ② 先復制index索引前的元素,再復制index索引后的元素,然后設置數組。

  • ③ 釋放鎖,返回舊值


例子

import java.util.Iterator; import java.util.concurrent.CopyOnWriteArrayList;class PutThread extends Thread {private CopyOnWriteArrayList<Integer> cowal;public PutThread(CopyOnWriteArrayList<Integer> cowal) {this.cowal = cowal;}public void run() {try {for (int i = 100; i < 110; i++) {cowal.add(i);Thread.sleep(50);}} catch (InterruptedException e) {e.printStackTrace();}} }public class CopyOnWriteArrayListDemo {public static void main(String[] args) {CopyOnWriteArrayList<Integer> cowal = new CopyOnWriteArrayList<Integer>();for (int i = 0; i < 10; i++) {cowal.add(i);}PutThread p1 = new PutThread(cowal);p1.start();Iterator<Integer> iterator = cowal.iterator();while (iterator.hasNext()) {System.out.print(iterator.next() + " ");}System.out.println();try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}iterator = cowal.iterator();while (iterator.hasNext()) {System.out.print(iterator.next() + " ");}} }

有一個PutThread線程會每隔50ms就向CopyOnWriteArrayList中添加一個元素,并且兩次使用了迭代器,迭代器輸出的內容都是生成迭代器時,CopyOnWriteArrayList的Object數組的快照的內容,在迭代的過程中,往CopyOnWriteArrayList中添加元素也不會拋出異常。

0 1 2 3 4 5 6 7 8 9 100 0 1 2 3 4 5 6 7 8 9 100 101 102 103

缺陷

  • 由于寫操作的時候,需要拷貝數組,會消耗內存,如果原數組的內容比較多的情況下,可能導致young gc或者full gc

  • 不能用于實時讀的場景,像拷貝數組、新增元素都需要時間,所以調用一個set操作后,讀取到數據可能還是舊的,雖然CopyOnWriteArrayList 能做到最終一致性,但是還是沒法滿足實時性要求;

  • 使用場景

    合適讀多寫少的場景,不過這類慎用

    誰也沒法保證CopyOnWriteArrayList 到底要放置多少數據,萬一數據稍微有點多,每次add/set都要重新復制數組,這個代價實在太高 ,容易引起故障

    總結

    以上是生活随笔為你收集整理的Java - Java集合中的安全失败Fail Safe机制 (CopyOnWriteArrayList)的全部內容,希望文章能夠幫你解決所遇到的問題。

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