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

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

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

一:前言

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

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

二:成員變量分析

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

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

三:源碼分析

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

1.空參構(gòu)造

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

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

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

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

如果不是,判斷Collection是否是ArrayList,如果是,那么就利用toArray()方法將其轉(zhuǎn)化為一個(gè)數(shù)組并賦值給成員變量array,否則將Collection里面的元素全部取出來copy到一個(gè)新數(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.傳入一個(gè)數(shù)組的構(gòu)造方法

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

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

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

  • add(E e)

首先加鎖,然后通過Arrays.copyOf()方法將元素copy到一個(gè)新的數(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到一個(gè)新數(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.在做修改操作的時(shí)候加鎖

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

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

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

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

相關(guān)文章

最新評論

汕尾市| 黑河市| 沧州市| 乌拉特前旗| 溆浦县| 驻马店市| 陈巴尔虎旗| 石嘴山市| 无棣县| 保定市| 诸城市| 广河县| 靖州| 临武县| 柳州市| 沙田区| 镇巴县| 营山县| 佛山市| 固始县| 剑阁县| 南城县| 山东省| 绍兴市| 津南区| 盐边县| 兴化市| 汨罗市| 呼和浩特市| 尉犁县| 应城市| 大宁县| 华蓥市| 通化市| 扎囊县| 浙江省| 贵定县| 阳城县| 新乡市| 夏河县| 广平县|