精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

阿里Java面試官:CopyOnWriteArrayList底層是怎么保證線程安全的?

開發 前端
CopyOnWriteArrayList是一種線程安全的ArrayList,底層是基于數組實現,不過該數組使用了volatile關鍵字修飾。 實現線程安全的原理是,“人如其名”,就是 Copy On Write(寫時復制),意思就是在對其進行修改操作的時候,復制一個新的ArrayList,在新的ArrayList上進行修改操作,從而不影響舊的ArrayList的讀操作。

歡迎學習解讀Java源碼專欄,在這個系列中,我將手把手帶著大家剖析Java核心組件的源碼,內容包含集合、線程、線程池、并發、隊列等,深入了解其背后的設計思想和實現細節,輕松應對工作面試。

引言

上篇文章提到ArrayList不是線程安全的,而CopyOnWriteArrayList是線程安全的。此刻我就會產生幾個問題:

  1. CopyOnWriteArrayList初始容量是多少?
  2. CopyOnWriteArrayList是怎么進行擴容的?
  3. CopyOnWriteArrayList是怎么保證線程安全的?

帶著這幾個問題,一起分析一下CopyOnWriteArrayList的源碼。

簡介

CopyOnWriteArrayList是一種線程安全的ArrayList,底層是基于數組實現,不過該數組使用了volatile關鍵字修飾。 實現線程安全的原理是,“人如其名”,就是 Copy On Write(寫時復制),意思就是在對其進行修改操作的時候,復制一個新的ArrayList,在新的ArrayList上進行修改操作,從而不影響舊的ArrayList的讀操作。 看一下源碼中CopyOnWriteArrayList內部有哪些數據結構組成:

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {

    // 加鎖,用來保證線程安全
    final transient ReentrantLock lock = new ReentrantLock();

    // 存儲元素的數組,使用了volatile修飾
    private transient volatile Object[] array;

    // 數組的get/set方法
    final Object[] getArray() {
        return array;
    }
    final void setArray(Object[] a) {
        array = a;
    }

}

CopyOnWriteArrayList的內部結構非常簡單,使用ReentrantLock加鎖,用來保證線程安全。使用數組存儲元素,數組使用volatile修飾,用來保證內存可見性。當其他線程重新對數組對象進行賦值的時候,當前線程可以及時感知到。

初始化

當我們調用CopyOnWriteArrayList的構造方法的時候,底層邏輯是怎么實現的?

List<Integer> list = new CopyOnWriteArrayList<>();

CopyOnWriteArrayList初始化的時候,不支持指定數組長度,接著往下看,就能明白CopyOnWriteArrayList為什么不支持指定數組長度。

public CopyOnWriteArrayList() {
    setArray(new Object[0]);
}

初始化過程非常簡單,就是創建了一個長度為0的數組。

添加元素

再看一下往CopyOnWriteArrayList添加元素時,調用的 add() 方法源碼實現:

// 添加元素
public boolean add(E e) {
    // 加鎖,保證線程安全
    final ReentrantLock lock = this.lock;
    lock.lock();

    try {
        // 獲取原數組
        Object[] elements = getArray();
        int len = elements.length;
        // 創建一個新數組,長度原數組長度+1,并把原數組元素拷貝到新數組里面
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        // 直接賦值給新數組末尾位置
        newElements[len] = e;
        // 替換原數組
        setArray(newElements);
        return true;
    } finally {
        // 釋放鎖
        lock.unlock();
    }
}

添加元素的流程:

  1. 先使用ReentrantLock加鎖,保證線程安全。
  2. 再創建一個新數組,長度是原數組長度+1,并把原數組元素拷貝到新數組里面。
  3. 然后在新數組末尾位置賦值
  4. 使用新數組替換掉原數組
  5. 最后釋放鎖

add() 方法添加元素的時候,并沒有在原數組上進行賦值,而是創建一個新數組,在新數組上賦值后,再用新數組替換原數組。這是為了利用volatile關鍵字的特性,如果直接在原數組上進行修改,其他線程是感知不到的。只有重新對原數組對象進行賦值,其他線程才能感知到。 還有一個需要注意的點是,每次添加元素的時候都會創建一個新數組,并涉及數組拷貝,相當于每次都進行擴容操作。當數組較大,性能消耗較為明顯。所以CopyOnWriteArrayList適用于讀多寫少的場景,如果存在較多的寫操作場景,性能也是一個需要考慮的因素。

刪除元素

再看一下刪除元素的方法 remove() 的源碼:

// 按照下標刪除元素
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) {
            // 0表示刪除的是數組末尾的元素
            setArray(Arrays.copyOf(elements, len - 1));
        } else {
            // 創建一個新數組,長度是原數組長度-1
            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();
    }
}

刪除元素的流程:

  1. 先使用ReentrantLock加鎖,保證線程安全。
  2. 再創建一個新數組,長度是原數組長度-1,并把原數組中剩余元素(不包含需要刪除的元素)拷貝到新數組里面。
  3. 使用新數組替換掉原數組
  4. 最后釋放鎖

可以看到,刪除元素的流程與添加元素的流程類似,都是需要創建一個新數組,再把舊數組元素拷貝到新數組,最后替換舊數組。區別就是新數組的長度不一樣,刪除元素流程中的新數組長度是舊數組長度-1,添加元素流程中的新數組長度是舊數組長度+1。 根據對象刪除元素的方法源碼與之類似,也是轉換成下標刪除,讀者可自行查看。

批量刪除

再看一下批量刪除元素方法 removeAll() 的源碼:

// 批量刪除元素
public boolean removeAll(Collection<?> c) {
    // 參數判空
    if (c == null) {
        throw new NullPointerException();
    }
    // 加鎖,保證線程安全
    final ReentrantLock lock = this.lock;
    lock.lock();

    try {
        // 獲取原數組
        Object[] elements = getArray();
        int len = elements.length;
        if (len != 0) {
            // 創建一個新數組,長度暫時使用原數組的長度,因為不知道要刪除多少個元素。
            Object[] temp = new Object[len];
            // newlen表示新數組中元素個數
            int newlen = 0;
            // 遍歷原數組,把需要保留的元素放到新數組中
            for (int i = 0; i < len; ++i) {
                Object element = elements[i];
                if (!c.contains(element)) {
                    temp[newlen++] = element;
                }
            }
            // 如果新數組沒有滿,就釋放空白位置,并覆蓋原數組
            if (newlen != len) {
                setArray(Arrays.copyOf(temp, newlen));
                return true;
            }
        }
        return false;
    } finally {
        // 釋放鎖
        lock.unlock();
    }
}

批量刪除元素的流程,與上面類似:

  1. 先使用ReentrantLock加鎖,保證線程安全。
  2. 再創建一個新數組,長度暫時使用原數組的長度,因為不知道要刪除多少個元素。
  3. 然后遍歷原數組,把需要保留的元素放到新數組中。
  4. 釋放掉新數組中空白位置,再使用新數組替換掉原數組。
  5. 最后釋放鎖

如果遇到需要一次刪除多個元素的場景,盡量使用 removeAll() 方法,因為 removeAll() 方法只涉及一次數組拷貝,性能比單個刪除元素更好。

并發修改問題

當遍歷CopyOnWriteArrayList的過程中,同時增刪CopyOnWriteArrayList中的元素,會發生什么情況?測試一下:

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class Test {

    public static void main(String[] args) {
        List<Integer> list = new CopyOnWriteArrayList<>();
        list.add(1);
        list.add(2);
        list.add(2);
        list.add(3);
        // 遍歷ArrayList
        for (Integer key : list) {
            // 判斷如果元素等于2,則刪除
            if (key.equals(2)) {
                list.remove(key);
            }
        }
        System.out.println(list);
    }

}

輸出結果:

[1, 3]

不但沒有拋出異常,還把CopyOnWriteArrayList中重復的元素也都刪除了。 原因是CopyOnWriteArrayList重新實現迭代器,拷貝了一份原數組的快照,在快照數組上進行遍歷。這樣做的優點是其他線程對數組的并發修改,不影響對快照數組的遍歷,但是遍歷過程中無法感知其他線程對數組修改,有得必有失。 下面是迭代器的源碼實現:

static final class COWIterator<E> implements ListIterator<E> {
    /**
     * 原數組的快照
     */
    private final Object[] snapshot;
    /**
     * 迭代游標
     */
    private int cursor;

    private COWIterator(Object[] elements, int initialCursor) {
        cursor = initialCursor;
        snapshot = elements;
    }

    public boolean hasNext() {
        return cursor < snapshot.length;
    }

    // 迭代下個元素
    public E next() {
        if (!hasNext())
            throw new NoSuchElementException();
        return (E)snapshot[cursor++];
    }
}

總結

現在可以回答文章開頭提出的問題了吧:

  1. CopyOnWriteArrayList初始容量是多少?

答案:是0

  1. CopyOnWriteArrayList是怎么進行擴容的?

答案:

  • 加鎖
  • 創建一個新數組,長度原數組長度+1,并把原數組元素拷貝到新數組里面。
  • 釋放鎖
  1. CopyOnWriteArrayList是怎么保證線程安全的?

答案:

  • 使用ReentrantLock加鎖,保證操作過程中線程安全。
  • 使用volatile關鍵字修飾數組,保證當前線程對數組對象重新賦值后,其他線程可以及時感知到。
責任編輯:武曉燕 來源: 一燈架構
相關推薦

2024-11-26 17:43:51

2023-11-27 08:32:02

元素HashMap

2022-02-08 08:14:07

Context數據線程

2021-05-13 07:58:05

HTTPSHTTP安全

2025-03-10 11:48:22

項目服務設計

2021-02-19 10:02:57

HTTPSJava安全

2025-04-08 00:00:00

@AsyncSpring異步

2021-09-27 07:11:18

MySQLACID特性

2020-10-26 07:07:50

線程安全框架

2021-09-07 10:44:33

Java 注解開發

2023-09-01 15:27:31

2024-02-28 10:14:47

Redis數據硬盤

2020-07-02 07:52:11

RedisHash映射

2024-03-14 14:56:22

反射Java數據庫連接

2022-04-29 08:17:38

RPC遠程代理代理模式

2024-02-29 16:49:20

volatileJava并發編程

2023-11-29 08:00:53

JavaTreeMap底層

2024-08-29 16:30:27

2020-03-10 08:01:05

Java堆內存線程共享

2023-02-08 07:04:20

死鎖面試官單元
點贊
收藏

51CTO技術棧公眾號

2019av中文字幕| 岛国精品视频在线播放| 91国产在线播放| 欧美一区免费观看| 91精品国产黑色瑜伽裤| 91在线视频官网| 国产精品日日做人人爱| 欧美精品一区二区成人| 一区二区美女| 91精品国产色综合久久ai换脸| 亚洲理论电影在线观看| 成年人视频网站在线| 国产精品亚洲а∨天堂免在线| 亚洲91av视频| 男女性高潮免费网站| 欧美丝袜足交| 欧美日韩国产成人在线91| 欧洲精品在线播放| 日本在线免费看| 91亚洲精华国产精华精华液| 成人免费看吃奶视频网站| 三级在线观看免费大全| 欧美人妖在线观看| 日韩一区二区三区在线视频| 亚洲国产精品久久久久婷蜜芽| 国产精品va在线观看视色 | 国产精品夜色7777狼人| 日本三级视频在线| 亚洲电影影音先锋| 亚洲丝袜av一区| xfplay5566色资源网站| 国产精品成人3p一区二区三区| 日本一区二区三区四区高清视频| 中文字幕国产在线观看| 欧美 亚欧 日韩视频在线| 亚洲天堂第一页| 少妇一级淫片免费放播放| 欧美国产中文高清| 欧美日韩免费在线视频| 777午夜精品免费视频| 亚洲 高清 成人 动漫| 视频在线观看入口黄最新永久免费国产| 亚洲国产电影在线观看| 久久精品成人一区二区三区蜜臀| 性欧美8khd高清极品| 极品销魂美女一区二区三区| 国产精品久久久久久久久久久新郎 | 天天干天天爽天天射| 成人勉费视频| 日韩欧美亚洲范冰冰与中字| 亚洲 欧美 日韩 国产综合 在线 | 91高潮在线观看| 日本少妇全体裸体洗澡| 亚洲一级高清| 韩日欧美一区二区| 亚洲日本韩国在线| 欧美亚洲视频| 日本成熟性欧美| 免费又黄又爽又猛大片午夜| 日韩电影一二三区| 国产精品福利在线观看| 中文字幕一区二区人妻| 久久成人免费网| 91在线观看免费高清| 国产老女人乱淫免费| 国产乱码一区二区三区| 俄罗斯精品一区二区| 图片区 小说区 区 亚洲五月| 99国内精品久久| 欧美一区观看| 黄页视频在线播放| 亚洲黄色性网站| 国产视频九色蝌蚪| 色香欲www7777综合网| 欧美亚洲图片小说| 亚洲三级在线观看视频| 一区二区三区自拍视频| 亚洲精品ady| 色噜噜在线观看| 成人中文视频| 欧美精品性视频| www日韩精品| 久久精品国产亚洲aⅴ| 亚洲va久久久噜噜噜| 男人天堂网在线视频| 久久久久久久久久久电影| 亚洲看片网站| www中文字幕在线观看| 色综合天天综合给合国产| www.com操| 加勒比色老久久爱综合网| 亚洲精品美女久久| 日韩精品久久久久久久的张开腿让| 91超碰国产精品| 亚洲97在线观看| 一卡二卡三卡在线观看| 成人免费黄色在线| 亚洲国产欧洲综合997久久| 色yeye免费人成网站在线观看| 午夜精品久久久久久久99水蜜桃 | 91大神在线观看线路一区| 欧美一卡在线观看| 18禁裸乳无遮挡啪啪无码免费| 99久久亚洲精品蜜臀| 97在线看福利| 国产剧情久久久| 国产亚洲视频系列| 亚洲中文字幕无码一区二区三区| 色老太综合网| 亚洲第一福利在线观看| www成人啪啪18软件| 亚洲人体偷拍| 亚洲最大成人免费视频| 黄色毛片在线观看| 午夜日韩在线电影| 欧美激情第四页| 中文字幕日韩三级| 久久99久久99| 欧美一区观看| 亚洲精品mv| 亚洲第一视频网| 欧美精品99久久久| 精品一区二区影视| 三级三级久久三级久久18| 黄色aa久久| 欧美va在线播放| 欧美精品久久久久久久久46p| 日韩综合小视频| 久久大片网站| 国内精彩免费自拍视频在线观看网址| 91精品国产91久久综合桃花 | 国产精品久久久久久久7电影| 天天操天天插天天射| 一区二区三区毛片| www.日本久久| 在线精品视频在线观看高清| 国产美女久久久| av免费在线一区二区三区| 欧美午夜美女看片| 黄色短视频在线观看| 亚洲激情一区| 国产在线精品日韩| av蜜臀在线| 亚洲黄色www网站| 日韩 国产 在线| www.亚洲精品| 久久亚洲中文字幕无码| 欧美尿孔扩张虐视频| 668精品在线视频| 日本一区视频| 91久久久免费一区二区| 真实乱视频国产免费观看| 丝瓜av网站精品一区二区| 欧美一级日本a级v片| 欧美人与性动交xxⅹxx| 尤物yw午夜国产精品视频| 精品久久久久久久久久久久久久久久久久| 2020日本不卡一区二区视频| 亚洲精品无码久久久久久| 久久99高清| 国产精品视频一区二区高潮| 在线观看美女网站大全免费| 欧美精品777| 青青草原免费观看| av在线不卡电影| 999精品网站| 99久久综合| 超碰在线97av| 小草在线视频免费播放| 国产亚洲精品美女| 91福利在线观看视频| 一区二区三区免费看视频| 日韩精品视频一区二区| 免费中文字幕日韩欧美| 翔田千里亚洲一二三区| 视频在线观看免费影院欧美meiju| 欧美激情精品久久久久久| 欧美亚洲日本| 欧美日韩高清在线播放| 五月天综合在线| 中文字幕欧美激情一区| 国产精品久久久久野外| 99精品国产一区二区青青牛奶| 秋霞在线观看一区二区三区| 国产aa精品| 热99在线视频| 伊人222成人综合网| 亚洲免费精彩视频| 国产又粗又大又爽| 粉嫩老牛aⅴ一区二区三区| 97在线观看免费视频| 国产福利精品一区| 国产裸体舞一区二区三区| 综合久久久久| 日韩影片在线播放| 北条麻妃在线一区二区免费播放| 国产成人免费av电影| 天堂av在线电影| 色综合伊人色综合网站| 色网站免费观看| 7878成人国产在线观看| aaa在线视频| 亚洲一区自拍偷拍| 永久免费毛片在线观看| av一区二区久久| 亚洲天堂av一区二区三区| 裸体一区二区| 久久综合久久久久| 999国产精品永久免费视频app| 国内外成人免费视频| 99综合久久| 国产精品草莓在线免费观看| av资源一区| 欧美成人在线网站| av在线电影网| 亚洲欧洲视频在线| 日韩中文字幕免费在线观看| 8v天堂国产在线一区二区| 手机av免费观看| 日韩欧美高清视频| 天天插天天操天天干| 一区二区三区在线看| 91麻豆精品久久毛片一级| 国产日韩精品一区二区浪潮av| 欧美激情 亚洲| 国产99一区视频免费| 亚洲 国产 图片| 日本中文字幕一区二区视频| 国产一区二区三区精彩视频| 亚洲小说欧美另类社区| 午夜久久久久久久久久久| 日韩在线理论| 日本高清久久一区二区三区| 亚洲第一二三区| 久草一区二区| 人人精品视频| 欧美不卡三区| 亚洲精品**不卡在线播he| 精品一区久久| 一区二区小说| 欧美亚洲精品日韩| 国内亚洲精品| 亚欧洲精品在线视频免费观看| 狠狠做深爱婷婷综合一区| 日本视频一区二区在线观看| 精品高清久久| 亚洲在线观看一区| 91超碰成人| 日韩精品一区二区在线视频 | 国产精品久久一卡二卡| 国产视频不卡在线| 亚洲欧洲av一区二区三区久久| 大胸美女被爆操| 国产精品福利影院| 欧美一级特黄高清视频| ㊣最新国产の精品bt伙计久久| 亚洲少妇xxx| 一区二区三区日韩| 激情综合网五月婷婷| 精品久久久久久国产91| 国产农村妇女aaaaa视频| 在线视频你懂得一区| 性色av一区二区三区四区| 欧美日韩成人在线| 午夜美女福利视频| 日韩精品极品在线观看| 国产片在线观看| www国产91| 国产白丝在线观看| 国产成人avxxxxx在线看| 国产成人免费精品| 不卡的av一区| 岳的好大精品一区二区三区| 亚洲欧洲日本国产| 国产精品xvideos88| 国产在线青青草| 精品亚洲免费视频| 精品一区二区视频在线观看| 久久久久国产一区二区三区四区 | 91日本视频在线| 黑人久久a级毛片免费观看| 欧美福利一区二区三区| 国产精品久久久久久久久久10秀 | 青娱乐91视频| 色婷婷精品大视频在线蜜桃视频| 一区二区日韩视频| 亚洲国产日韩欧美在线动漫 | 欧美极品少妇xxxxx| 亚洲永久av| 3d动漫啪啪精品一区二区免费 | 五月天婷婷久久| 91精品国产91久久久久久一区二区 | 国产精品12345| 美腿丝袜亚洲色图| 亚洲av成人精品一区二区三区| 欧美韩国一区二区| 日本少妇xxxx动漫| 欧美一区二区精品| 可以免费看污视频的网站在线| 欧美成年人视频| 99re66热这里只有精品4| 国产精品二区三区四区| 日韩精品二区| 亚洲精品无码久久久久久| 国产成人小视频| 黄色激情小视频| 一本大道久久a久久综合| 亚洲国产剧情在线观看| 中文字幕在线日韩 | 国产伦精品一区| 国产精品传媒精东影业在线| 嫩草av久久伊人妇女超级a| 不卡av电影在线播放| 国产精品三区在线观看| 在线精品视频免费播放| 天堂在线观看视频| 欧美人与物videos| 国产精品一区免费在线 | 青青草福利视频| 亚洲高清免费观看| www.久久久久久| 久久影院在线观看| 四虎在线精品| 一区在线电影| 免费日本视频一区| 四虎永久免费在线观看| 欧美日韩亚洲一区二| 日日夜夜精品免费| 久久久久久久一| 136导航精品福利| 黄网站色视频免费观看| 国产伦精品一区二区三区免费| 日本美女黄色一级片| 欧美日韩一区二区三区在线看| 蝌蚪视频在线播放| 国产激情久久久| 精品一二三区| 91国产精品视频在线观看| 国产女人18毛片水真多成人如厕| 91视频免费网址| 亚洲美女自拍视频| 影视一区二区三区| 视频一区二区精品| 欧美aaaaaa午夜精品| 青青草华人在线视频| 欧美日韩一区二区在线观看| 嫩草香蕉在线91一二三区| 91美女高潮出水| 欧美日韩国产综合网| 扒开伸进免费视频| 五月婷婷激情综合网| 青青国产在线| 国产精品久久91| 2023国产精品久久久精品双| 亚洲精品在线网址| 亚洲国产精品欧美一二99| 深爱激情五月婷婷| 欧美中文字幕在线| 奇米影视亚洲| 天天综合成人网| 亚洲国产成人va在线观看天堂| 人妻精品无码一区二区| 99精品桃花视频在线观看| 成人免费精品动漫网站| 日韩欧美一区中文| sm捆绑调教国产免费网站在线观看 | heyzo国产| 国产日韩av一区二区| 国产又爽又黄又嫩又猛又粗| 欧美大片免费看| 日韩激情毛片| 九九热精品在线| 一区二区在线免费播放| 精品视频免费在线播放| 国产亚洲女人久久久久毛片| 在线观看亚洲国产| 精品中文字幕在线| 你懂的一区二区三区| 手机在线国产视频| 亚洲一级二级在线| 国产尤物视频在线| 99re在线国产| 日韩在线观看一区二区| 午夜国产福利一区二区| 日韩成人在线视频网站| 精品久久毛片| 国产午夜福利100集发布| 国产农村妇女毛片精品久久麻豆| www.com欧美| 国产精品色悠悠| 亚洲欧洲一区| 中国1级黄色片| 日韩黄色在线免费观看| 成人国产精品久久| 亚洲色成人一区二区三区小说| 国产精品灌醉下药二区| 日本1级在线| 国产精品一国产精品最新章节| 日韩黄色片在线观看| 国产美女福利视频|