最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中的CopyOnWriteArrayList容器解析

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

1. 簡介

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

2. 原理

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

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

在這里插入圖片描述

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

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

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

private transient volatile Object[] array;

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

3. 優(yōu)點(diǎn)

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

4. 缺點(diǎn)

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

二是無法保證實(shí)時(shí)性,因?yàn)镃opyOnWrite的寫時(shí)復(fù)制機(jī)制,所以在進(jìn)行寫操作的時(shí)候,內(nèi)存里會(huì)同時(shí)駐扎兩個(gè)對(duì)象的內(nèi)存,舊的對(duì)象和新寫入的對(duì)象(注意:在復(fù)制的時(shí)候只是復(fù)制容器里的引用,只是在寫的時(shí)候會(huì)創(chuàng)建新對(duì)象添加到新容器里,而舊容器的對(duì)象還在使用,所以有兩份對(duì)象內(nèi)存)。

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í)行寫操作,之后再切換引用。當(dāng)然此過程是要加鎖的。

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)
                //如果要?jiǎng)h除的是列表末端數(shù)據(jù),拷貝前l(fā)en-1個(gè)數(shù)據(jù)到新副本上,再切換引用
                setArray(Arrays.copyOf(elements, len - 1));
            else {
                //否則,將除要?jiǎng)h除元素之外的其他元素拷貝到新副本中,并切換引用
                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();
        }
    }

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

我們?cè)賮砜纯醋x操作,CopyOnWriteArrayList的讀操作是不用加鎖的,性能很高。

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

直接讀取即可,無需加鎖

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

5.3 弱一致性的迭代器

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

// 演示多線程下迭代器的弱一致性結(jié)果
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中下標(biāo)為1的元素為ali
                arrayList.set(1, "ali");
                // 刪除元素
                arrayList.remove(2);
                arrayList.remove(3);
            }
        });
        // 保證在修改線程啟動(dòng)前獲取迭代器
        Iterator<String> itr = arrayList.iterator();
        // 啟動(dòng)線程
        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

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

6. ArrayList轉(zhuǎn)為線程安全的方法

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

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

相關(guān)文章

  • SpringBoot?整合?Quartz?定時(shí)任務(wù)框架詳解

    SpringBoot?整合?Quartz?定時(shí)任務(wù)框架詳解

    這篇文章主要介紹了SpringBoot整合Quartz定時(shí)任務(wù)框架詳解,Quartz是一個(gè)完全由Java編寫的開源作業(yè)調(diào)度框架,為在Java應(yīng)用程序中進(jìn)行作業(yè)調(diào)度提供了簡單卻強(qiáng)大的機(jī)制
    2022-08-08
  • 詳解Java設(shè)計(jì)模式之橋接模式

    詳解Java設(shè)計(jì)模式之橋接模式

    橋接,顧名思義,就是用來連接兩個(gè)部分,使得兩個(gè)部分可以互相通訊。橋接模式將系統(tǒng)的抽象部分與實(shí)現(xiàn)部分分離解耦,使他們可以獨(dú)立的變化。本文通過示例詳細(xì)介紹了橋接模式的原理與使用,需要的可以參考一下
    2022-06-06
  • Eclipse運(yùn)行android項(xiàng)目報(bào)錯(cuò)Unable to build: the file dx.jar was not loaded from the SDK folder的解決辦法

    Eclipse運(yùn)行android項(xiàng)目報(bào)錯(cuò)Unable to build: the file dx.jar was not

    今天小編就為大家分享一篇關(guān)于Eclipse運(yùn)行android項(xiàng)目報(bào)錯(cuò)Unable to build: the file dx.jar was not loaded from the SDK folder的解決辦法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • 10個(gè)SpringBoot框架內(nèi)置的實(shí)用功能詳解

    10個(gè)SpringBoot框架內(nèi)置的實(shí)用功能詳解

    在 Spring Boot 開發(fā)中,框架內(nèi)置的諸多實(shí)用功能猶如一把把利刃,能讓開發(fā)者在項(xiàng)目的各個(gè)階段都事半功倍,這些功能無需額外集成,通過簡單配置或編碼即可快速實(shí)現(xiàn)常見需求,下面將為你深入解析一系列極具價(jià)值的內(nèi)置功能,需要的朋友可以參考下
    2025-06-06
  • SpringBoot數(shù)據(jù)庫初始化datasource配置方式

    SpringBoot數(shù)據(jù)庫初始化datasource配置方式

    這篇文章主要為大家介紹了SpringBoot數(shù)據(jù)庫初始化datasource配置方式,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • 深入了解Java設(shè)計(jì)模式之職責(zé)鏈模式

    深入了解Java設(shè)計(jì)模式之職責(zé)鏈模式

    Java設(shè)計(jì)模式中有很多種類別,例如單例模式、裝飾模式、觀察者模式等。本文將為大家詳細(xì)介紹其中的職責(zé)鏈模式,感興趣的可以了解一下
    2022-09-09
  • Lombok中@Builder和@SuperBuilder注解的用法案例

    Lombok中@Builder和@SuperBuilder注解的用法案例

    @Builder?是?lombok?中的注解,可以使用builder()構(gòu)造的Person.PersonBuilder對(duì)象進(jìn)行鏈?zhǔn)秸{(diào)用,給所有屬性依次賦值,這篇文章主要介紹了Lombok中@Builder和@SuperBuilder注解的用法,需要的朋友可以參考下
    2023-01-01
  • Mybatis查詢時(shí),區(qū)分大小寫操作

    Mybatis查詢時(shí),區(qū)分大小寫操作

    這篇文章主要介紹了Mybatis查詢時(shí),區(qū)分大小寫操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • SpringBoot導(dǎo)出Excel表格到指定路徑的代碼詳解

    SpringBoot導(dǎo)出Excel表格到指定路徑的代碼詳解

    Spring Boot導(dǎo)出Excel通常涉及到使用第三方庫如Apache POI或者XlsxWriter等,它們能幫助你在Spring應(yīng)用中生成并下載Excel文件,那么SpringBoot如何導(dǎo)出Excel表格到指定路徑,本文將給大家詳細(xì)的介紹一下
    2024-07-07
  • IDEA 創(chuàng)建多級(jí)文件夾的操作

    IDEA 創(chuàng)建多級(jí)文件夾的操作

    這篇文章主要介紹了IDEA 創(chuàng)建多級(jí)文件夾的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02

最新評(píng)論

连城县| 徐州市| 西贡区| 青神县| 台前县| 无为县| 兴和县| 华阴市| 邢台市| 邹城市| 五寨县| 贵州省| 安顺市| 嵊泗县| 克山县| 南皮县| 南郑县| 克拉玛依市| 公主岭市| 博爱县| 明光市| 永仁县| 安乡县| 舞钢市| 湘阴县| 寻乌县| 梁平县| 梅河口市| 肥乡县| 曲麻莱县| 团风县| 项城市| 永仁县| 晴隆县| 旬阳县| 陆河县| 新安县| 罗城| 伊宁县| 石泉县| 北辰区|