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

詳解CopyOnWriteArrayList是如何保證線程安全

 更新時間:2022年09月15日 16:11:54   作者:沉迷學(xué)習(xí)的小伙  
這篇文章主要為大家介紹了CopyOnWriteArrayList是如何保證線程安全講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

一:前言

在我們需要保證線程安全的時候,如果使用到Map,那么我們可以使用線程安全的ConcurrentHashMap,ConcurrentHashMap不僅可以保證線程安全,而且效率也非常不錯,那有沒有線程安全的List呢?

答案是有,那就是CopyOnWriteArrayList。今天我們就一起來了解一下CopyOnWriteArrayList,看它是如何巧妙的保證線程安全的吧。

二:成員變量分析

    //進(jìn)行修改操作時的鎖
    final transient ReentrantLock lock = new ReentrantLock();

    //真正保存數(shù)據(jù)的數(shù)組 用volatile關(guān)鍵字進(jìn)行修飾,保證array的引用的可見性
    private transient volatile Object[] array;

三:源碼分析

首先我們看構(gòu)造方法,CopyOnWriteArrayList有三個構(gòu)造方法。

1.空參構(gòu)造

調(diào)用setArray方法將成員變量array賦值為一個長度為0的數(shù)組。

public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }
final void setArray(Object[] a) {
        array = a;
    }

2.傳入一個Collection對象的構(gòu)造方法

首先判斷Collection是否是一個CopyOnWriteArrayList,如果是,直接將傳入的CopyOnWriteArrayList的elements重新賦值給需要創(chuàng)建的CopyOnWriteArrayList。

如果不是,判斷Collection是否是ArrayList,如果是,那么就利用toArray()方法將其轉(zhuǎn)化為一個數(shù)組并賦值給成員變量array,否則將Collection里面的元素全部取出來copy到一個新數(shù)組中,并且將該數(shù)組賦值給成員變量array。

    public CopyOnWriteArrayList(Collection<? extends E> c) {
        Object[] elements;
        if (c.getClass() == CopyOnWriteArrayList.class)
            elements = ((CopyOnWriteArrayList<?>)c).getArray();
        else {
            elements = c.toArray();
            if (c.getClass() != ArrayList.class)
                elements = Arrays.copyOf(elements, elements.length, Object[].class);
        }
        setArray(elements);
    }

3.傳入一個數(shù)組的構(gòu)造方法

將傳入的數(shù)組的元素copy到一個新的Object數(shù)組,并且賦值給成員變量array。

    public CopyOnWriteArrayList(E[] toCopyIn) {
        setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
    }

接下來我們看核心的add(),remove(),get()方法。

  • add(E e)

首先加鎖,然后通過Arrays.copyOf()方法將元素copy到一個新的數(shù)組中,新的數(shù)組的長度為原數(shù)組的長度+1,并且將需要加入的元素賦值到新數(shù)組的最后。最后將新數(shù)組賦值給成員變量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();
        }
    }
  • add(int index, E element)

add(int index, E element)方法需要將元素加入到指定的索引位置中。首先也是先加鎖,保證線程安全,將原數(shù)組分為兩段進(jìn)行操作,根據(jù)index進(jìn)行分隔,分別copy index之前的元素和之后的元素,copy完成之后在將需要插入的元素設(shè)置到索引為index的位置上。之后將新數(shù)組賦值給成員變量array。

public void add(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (index > len || index < 0)
                throw new IndexOutOfBoundsException("Index: "+index+
                                                    ", Size: "+len);
            Object[] newElements;
            int numMoved = len - index;
            if (numMoved == 0)
                newElements = Arrays.copyOf(elements, len + 1);
            else {
                newElements = new Object[len + 1];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index, newElements, index + 1,
                                 numMoved);
            }
            newElements[index] = element;
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

接下來看remove()方法。

  • remove(int index)

remove(int index)方法需要在數(shù)組中移除指定索引的值。首先是加鎖,同樣也是將原數(shù)組分為兩段進(jìn)行操作,根據(jù)index進(jìn)行分隔,分別copy index之前的元素和之后的元素,copy到一個新數(shù)組中,新數(shù)組的長度為原數(shù)組的長度減一(注意這里是沒有copy index索引位置的值的,所以相當(dāng)于移除了index索引上的值)。之后將新數(shù)組賦值給成員變量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();
        }
    }

接下來看get()方法

  • get()

我們可以看到get()方法很簡單,就是從array成員變量中取出對應(yīng)索引的值。并沒有加鎖處理。所以盡管是在并發(fā)高的情況下,get()方法的效率依舊是比較高的。

    /**
     * {@inheritDoc}
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        return get(getArray(), index);
    }
    
    private E get(Object[] a, int index) {
        return (E) a[index];
    }

四:總結(jié)

CopyOnWriteArrayList為什么能夠保證線程安全,主要是因?yàn)橐韵聨c(diǎn):

1.在做修改操作的時候加鎖

2.每次修改都是將元素copy到一個新的數(shù)組中,并且將數(shù)組賦值到成員變量array中。

3.利用volatile關(guān)鍵字修飾成員變量array,這樣就可以保證array的引用的可見性,每次修改之前都能夠拿到最新的array引用。這點(diǎn)很關(guān)鍵。

看到這里,相信你已經(jīng)對CopyOnWriteArrayList非常了解了,CopyOnWriteArrayList在查詢多,修改操作少的情況下效率是非??捎^的,既能夠保證線程安全,又能有不錯的效率。但是如果修改操作較多,就會導(dǎo)致數(shù)組頻繁的copy,效率就會有所下降,如果修改操作很多,那么直接使用Collections.synchronizedList(),或許也是一個不錯的選擇。

以上就是詳解CopyOnWriteArrayList是如何保證線程安全的詳細(xì)內(nèi)容,更多關(guān)于CopyOnWriteArrayList 線程安全的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Springboot @RequestBody注解踩坑記錄

    Springboot @RequestBody注解踩坑記錄

    這篇文章主要介紹了Springboot @RequestBody注解踩坑記錄,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java NIO:淺析IO模型_動力節(jié)點(diǎn)Java學(xué)院整理

    Java NIO:淺析IO模型_動力節(jié)點(diǎn)Java學(xué)院整理

    在進(jìn)入Java NIO編程之前,我們今天先來討論一些比較基礎(chǔ)的知識:I/O模型。對java io nio相關(guān)知識感興趣的朋友一起學(xué)習(xí)吧
    2017-05-05
  • Java web velocity分頁宏示例

    Java web velocity分頁宏示例

    這篇文章主要介紹了Java web velocity分頁宏示例,需要的朋友可以參考下
    2014-03-03
  • Springboot打包部署代碼實(shí)例

    Springboot打包部署代碼實(shí)例

    這篇文章主要介紹了Springboot打包部署代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-01-01
  • 深入了解MyBatis參數(shù)

    深入了解MyBatis參數(shù)

    今天小編就為大家分享一篇關(guān)于深入了解MyBatis參數(shù),小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • 基于Properties實(shí)現(xiàn)配置數(shù)據(jù)庫驅(qū)動

    基于Properties實(shí)現(xiàn)配置數(shù)據(jù)庫驅(qū)動

    這篇文章主要介紹了基于Properties實(shí)現(xiàn)配置數(shù)據(jù)庫驅(qū)動,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-05-05
  • java.util.NoSuchElementException原因及兩種解決方法

    java.util.NoSuchElementException原因及兩種解決方法

    本文主要介紹了java.util.NoSuchElementException原因及兩種解決方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • 一文詳解Java中的注解(Annotation)

    一文詳解Java中的注解(Annotation)

    在Java中注解(Annotation)引入始于Java5,用來描述Java代碼的元信息,通常情況下注解不會直接影響代碼的執(zhí)行,盡管有些注解可以用來做到影響代碼執(zhí)行,這篇文章主要給大家介紹了關(guān)于Java中注解(Annotation)的相關(guān)資料,需要的朋友可以參考下
    2024-03-03
  • Java執(zhí)行SQL腳本文件到數(shù)據(jù)庫詳解

    Java執(zhí)行SQL腳本文件到數(shù)據(jù)庫詳解

    這篇文章主要為大家詳細(xì)介紹了Java執(zhí)行SQL腳本文件到數(shù)據(jù)庫的相關(guān)方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • 淺談Spring學(xué)習(xí)之request,session與globalSession作用域

    淺談Spring學(xué)習(xí)之request,session與globalSession作用域

    這篇文章主要介紹了Spring學(xué)習(xí)之request,session與globalSession作用域的相關(guān)內(nèi)容,需要的朋友可以參考下。
    2017-09-09

最新評論