日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

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

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

文章目錄

  • Pre
  • 概述
  • fail-safe的容器—CopyOnWriteArrayList
    • add
    • remove函數(shù)
  • 例子
  • 缺陷
  • 使用場(chǎng)景

Pre

Java - Java集合中的快速失敗Fail Fast 機(jī)制

概述

ArrayList使用fail-fast機(jī)制自然是因?yàn)樗鰪?qiáng)了數(shù)據(jù)的安全性。

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

采用安全失敗機(jī)制的集合容器,在 Iterator 的實(shí)現(xiàn)上沒(méi)有設(shè)計(jì)拋出 ConcurrentModificationException 的代碼段,從而避免了fail-fast。


fail-safe的容器—CopyOnWriteArrayList

寫(xiě)時(shí)復(fù)制: 當(dāng)我們往一個(gè)容器添加元素的時(shí)候,先將當(dāng)前容器復(fù)制出一個(gè)新的容器,然后新的容器里添加元素,添加完元素之后,再將原容器的引用指向新的容器。

好處是我們可以對(duì)CopyOnWrite容器進(jìn)行并發(fā)的讀,而不需要加鎖,因?yàn)楫?dāng)前容器不會(huì)添加任何元素。所以CopyOnWrite容器也是一種讀寫(xiě)分離的思想,讀和寫(xiě)不同的容器。


add

public boolean add(E e) {// 可重入鎖final ReentrantLock lock = this.lock;// 獲取鎖lock.lock();try {// 元素?cái)?shù)組Object[] elements = getArray();// 數(shù)組長(zhǎng)度int len = elements.length;// 復(fù)制數(shù)組Object[] newElements = Arrays.copyOf(elements, len + 1);// 存放元素enewElements[len] = e;// 設(shè)置數(shù)組setArray(newElements);return true;} finally {// 釋放鎖lock.unlock();} }

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

  • 獲取鎖(保證多線程的安全訪問(wèn)),獲取當(dāng)前的Object數(shù)組,獲取Object數(shù)組的長(zhǎng)度為length,進(jìn)入步驟②。
  • 根據(jù)Object數(shù)組復(fù)制一個(gè)長(zhǎng)度為length+1的Object數(shù)組為newElements(此時(shí),newElements[length]為null),進(jìn)入下一步驟。
  • 將下標(biāo)為length的數(shù)組元素newElements[length]設(shè)置為元素e,再設(shè)置當(dāng)前Object[]為newElements,釋放鎖,返回。這樣就完成了元素的添加。

remove函數(shù)

public E remove(int index) {// 可重入鎖final ReentrantLock lock = this.lock;// 獲取鎖lock.lock();try {// 獲取數(shù)組Object[] elements = getArray();// 數(shù)組長(zhǎng)度int len = elements.length;// 獲取舊值E oldValue = get(elements, index);// 需要移動(dòng)的元素個(gè)數(shù)int numMoved = len - index - 1;if (numMoved == 0) // 移動(dòng)個(gè)數(shù)為0// 復(fù)制后設(shè)置數(shù)組setArray(Arrays.copyOf(elements, len - 1));else { // 移動(dòng)個(gè)數(shù)不為0// 新生數(shù)組Object[] newElements = new Object[len - 1];// 復(fù)制index索引之前的元素System.arraycopy(elements, 0, newElements, 0, index);// 復(fù)制index索引之后的元素System.arraycopy(elements, index + 1, newElements, index,numMoved);// 設(shè)置索引setArray(newElements);}// 返回舊值return oldValue;} finally {// 釋放鎖lock.unlock();} }
  • ①獲取鎖,獲取數(shù)組elements,數(shù)組長(zhǎng)度為length,獲取索引的值elements[index],計(jì)算需要移動(dòng)的元素個(gè)數(shù)(length - index - 1),若個(gè)數(shù)為0,則表示移除的是數(shù)組的最后一個(gè)元素,復(fù)制elements數(shù)組,復(fù)制長(zhǎng)度為length-1,然后設(shè)置數(shù)組,進(jìn)入步驟③;否則,進(jìn)入步驟②

  • ② 先復(fù)制index索引前的元素,再?gòu)?fù)制index索引后的元素,然后設(shè)置數(shù)組。

  • ③ 釋放鎖,返回舊值


例子

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() + " ");}} }

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

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

缺陷

  • 由于寫(xiě)操作的時(shí)候,需要拷貝數(shù)組,會(huì)消耗內(nèi)存,如果原數(shù)組的內(nèi)容比較多的情況下,可能導(dǎo)致young gc或者full gc

  • 不能用于實(shí)時(shí)讀的場(chǎng)景,像拷貝數(shù)組、新增元素都需要時(shí)間,所以調(diào)用一個(gè)set操作后,讀取到數(shù)據(jù)可能還是舊的,雖然CopyOnWriteArrayList 能做到最終一致性,但是還是沒(méi)法滿(mǎn)足實(shí)時(shí)性要求;

  • 使用場(chǎng)景

    合適讀多寫(xiě)少的場(chǎng)景,不過(guò)這類(lèi)慎用

    誰(shuí)也沒(méi)法保證CopyOnWriteArrayList 到底要放置多少數(shù)據(jù),萬(wàn)一數(shù)據(jù)稍微有點(diǎn)多,每次add/set都要重新復(fù)制數(shù)組,這個(gè)代價(jià)實(shí)在太高 ,容易引起故障

    總結(jié)

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

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。