欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中的CopyOnWriteArrayList容器解析

 更新時間:2023年12月19日 08:32:33   作者:GeGe&YoYo  
這篇文章主要介紹了Java中的CopyOnWriteArrayList容器解析,CopyOnWriteArrayList容器允許并發(fā)讀,讀操作是無鎖的,性能較高。至于寫操作,比如向容器中添加一個元素,則首先將當前容器復制一份,然后在新副本上執(zhí)行寫操作,需要的朋友可以參考下

1. 簡介

在 ArrayList 的類注釋上,JDK 就提醒了我們,如果要把 ArrayList 作為共享變量的話,是線程不安全的,推薦我們自己加鎖或者使用 Collections.synchronizedList 方法,其實 JDK 還提供了另外一種線程安全的 List,叫做 CopyOnWriteArrayList

2. 原理

很多時候,我們的系統(tǒng)應對的都是讀多寫少的并發(fā)場景。CopyOnWriteArrayList容器允許并發(fā)讀,讀操作是無鎖的,性能較高。至于寫操作,比如向容器中添加一個元素,則首先將當前容器復制一份,然后在新副本上執(zhí)行寫操作,結束之后再將原容器的引用指向新容器。

  • 線程安全的,多線程環(huán)境下可以直接使用,無需加鎖;
  • 通過鎖 + 數(shù)組拷貝 + volatile 關鍵字保證了線程安全;
  • 每次數(shù)組操作,都會把數(shù)組拷貝一份出來,在新數(shù)組上進行操作,操作成功之后再賦值回去。

在這里插入圖片描述

從整體架構上來說,CopyOnWriteArrayList 數(shù)據(jù)結構和 ArrayList 是一致的,底層是個數(shù)組,只不過 CopyOnWriteArrayList 在對數(shù)組進行操作的時候,基本會分四步走:

  • 加鎖;
  • 從原數(shù)組中拷貝出新數(shù)組;
  • 在新數(shù)組上進行操作,并把新數(shù)組賦值給數(shù)組容器;
  • 解鎖

除了加鎖之外,CopyOnWriteArrayList 的底層數(shù)組還被 volatile 關鍵字修飾,意思是一旦數(shù)組被修改,其它線程立馬能夠感知到,代碼如下:

private transient volatile Object[] array;

整體上來說,CopyOnWriteArrayList 就是利用鎖 + 數(shù)組拷貝 + volatile 關鍵字保證了 List 的線程安全。

3. 優(yōu)點

讀操作(不加鎖)性能很高,因為無需任何同步措施,比較適用于讀多寫少的并發(fā)場景。Java的list在遍歷時,若中途有別的線程對list容器進行修改,則會拋ConcurrentModificationException異常。而CopyOnWriteArrayList由于其"讀寫分離"的思想,遍歷和修改操作分別作用在不同的list容器,所以在使用迭代器進行遍歷時候,也就不會拋出ConcurrentModificationException異常了。

4. 缺點

一是內存占用問題,畢竟每次執(zhí)行寫操作都要將原容器拷貝一份。數(shù)據(jù)量大時,對內存壓力較大,可能會引起頻繁GC;

二是無法保證實時性,因為CopyOnWrite的寫時復制機制,所以在進行寫操作的時候,內存里會同時駐扎兩個對象的內存,舊的對象和新寫入的對象(注意:在復制的時候只是復制容器里的引用,只是在寫的時候會創(chuàng)建新對象添加到新容器里,而舊容器的對象還在使用,所以有兩份對象內存)。

5. 源碼分析

5.1 添加操作

public boolean add(E e) {
        //ReentrantLock加鎖,保證線程安全
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            //拷貝原容器,長度為原容器長度加一
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            //在新副本上執(zhí)行添加操作
            newElements[len] = e;
            //將原容器引用指向新副本
            setArray(newElements);
            return true;
        } finally {
            //解鎖
            lock.unlock();
        }
    }

添加的邏輯很簡單,先將原容器copy一份,然后在新副本上執(zhí)行寫操作,之后再切換引用。當然此過程是要加鎖的。

5.2 刪除操作

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)
                //如果要刪除的是列表末端數(shù)據(jù),拷貝前l(fā)en-1個數(shù)據(jù)到新副本上,再切換引用
                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();
        }
    }

刪除操作同理,將除要刪除元素之外的其他元素拷貝到新副本中,然后切換引用,將原容器引用指向新副本。同屬寫操作,需要加鎖。

我們再來看看讀操作,CopyOnWriteArrayList的讀操作是不用加鎖的,性能很高。

public E get(int index) {
        return get(getArray(), index);
    }

直接讀取即可,無需加鎖

 	private E get(Object[] a, int index) {
        return (E) a[index];
    }

5.3 弱一致性的迭代器

所謂弱一致性是指返回迭代器后,其他線程對list的增刪改查對迭代器是不可見的

// 演示多線程下迭代器的弱一致性結果
public class copylist {
    private static volatile CopyOnWriteArrayList<String> arrayList = new CopyOnWriteArrayList<>();
    public static void main(String[] args) throws InterruptedException {
        arrayList.add("hello");
        arrayList.add("alibaba");
        arrayList.add("welcome");
        arrayList.add("to");
        arrayList.add("hangzhou");
        Thread threadOne = new Thread(new Runnable() {
            @Override
            public void run() {
                // 修改list中下標為1的元素為ali
                arrayList.set(1, "ali");
                // 刪除元素
                arrayList.remove(2);
                arrayList.remove(3);
            }
        });
        // 保證在修改線程啟動前獲取迭代器
        Iterator<String> itr = arrayList.iterator();
        // 啟動線程
        threadOne.start();
        // 等待子線程執(zhí)行完畢
        threadOne.join();
        while(itr.hasNext()) {
            System.out.println(itr.next());
        }
    }
}

執(zhí)行程序:

hello
alibaba
welcome
to
hangzhou
 
Process finished with exit code 0

從輸出結果我們知道,在子線程里面進行的操作一個都沒有生效,這就是迭代器弱一致性的體現(xiàn)。需要注意的是,獲取迭代器的操作必須在子線程操作之前進行。

6. ArrayList轉為線程安全的方法

List list = Collections.synchronizedList(new ArrayList());

到此這篇關于Java中的CopyOnWriteArrayList容器解析的文章就介紹到這了,更多相關CopyOnWriteArrayList解析內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Spring Schedule Task動態(tài)改寫Cron配置方式

    Spring Schedule Task動態(tài)改寫Cron配置方式

    這篇文章主要介紹了Spring Schedule Task動態(tài)改寫Cron配置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java虛擬機精選面試題20道

    Java虛擬機精選面試題20道

    現(xiàn)在面試Java開發(fā)時,基本都會問到Java虛擬機的知識,根據(jù)職位不同問的內容深淺又有所區(qū)別。本文整理了10道面試中常問的Java虛擬機面試題,希望對正在面試的同學有所幫助
    2021-06-06
  • Spring使用注解方式處理事務

    Spring使用注解方式處理事務

    這篇文章主要為大家詳細介紹了Spring使用注解方式處理事務,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • maven profile自動切換環(huán)境參數(shù)的2種方法詳解

    maven profile自動切換環(huán)境參數(shù)的2種方法詳解

    這篇文章主要給大家介紹了關于maven profile自動切換環(huán)境參數(shù)的2種方法,文中通過示例代碼將這兩種方法介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。
    2018-04-04
  • 分布式面試消息隊列解決消息重復保證消息順序

    分布式面試消息隊列解決消息重復保證消息順序

    這篇文章主要介紹了分布式面試問題:分布式消息隊列如何解決消息重復并保證消息順序面試問題解答,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步
    2022-03-03
  • Java未賦值變量的初始值解析(默認值)

    Java未賦值變量的初始值解析(默認值)

    這篇文章主要介紹了Java未賦值變量的初始值(默認值),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • 微信開發(fā)之使用java獲取簽名signature

    微信開發(fā)之使用java獲取簽名signature

    這篇文章主要為大家詳細介紹了微信開發(fā)之使用java獲取簽名signature,感興趣的小伙伴們可以參考一下
    2016-08-08
  • 詳解Spring Cloud Zuul中路由配置細節(jié)

    詳解Spring Cloud Zuul中路由配置細節(jié)

    本篇文章主要介紹了詳解Spring Cloud Zuul中路由配置細節(jié),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • SpringMVC注解的入門實例詳解

    SpringMVC注解的入門實例詳解

    這篇文章主要為大家介紹了SpringMVC注解的入門實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • javacv ffmpeg使用原生ffmpeg命令方式

    javacv ffmpeg使用原生ffmpeg命令方式

    在使用javacv集成ffmpeg進行視頻處理時,發(fā)現(xiàn)使用ffmpeg-6.0-1.5.9版本出現(xiàn)原生命令執(zhí)行失敗的問題,通過降級至ffmpeg-5.1.2-1.5.8版本,問題得到解決,此外,ffprobe可以用于獲取視頻屬性,需確保視頻片段屬性一致性
    2024-11-11

最新評論