精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

电影在线观看一区| 中文区中文字幕免费看| 精品资源在线| 色成年激情久久综合| 日本一区二区三区免费看| 一区不卡在线观看| 亚洲黄色高清| 在线视频一区二区| 久久久久亚洲av无码专区首jn| av资源在线| 国产精品免费看片| 国产伦精品一区二区三区视频免费| 日韩一区二区视频在线| 午夜欧美在线| 亚洲另类图片色| 99精品视频免费版的特色功能| 午夜伦理福利在线| 亚洲欧美视频在线观看视频| 欧美13一14另类| 亚洲第一色网站| 九一久久久久久| 日本一区二区三区四区视频| 玖玖爱免费视频| 欧美国产一级| 日韩精品视频在线免费观看| 91网址在线观看精品| 伊人久久国产| 亚洲第一狼人社区| 少妇高潮流白浆| 成人动漫在线播放| 91毛片在线观看| 91香蕉视频在线下载| 中文字幕有码无码人妻av蜜桃| 在线视频免费在线观看一区二区| 综合国产视频| 中文字幕亚洲一区二区va在线| 久久久久一区二区| 国模私拍视频在线| 国产乱码精品一区二区三| 国产精品日韩在线观看| 狠狠人妻久久久久久综合| 黄色免费成人| 欧美激情精品久久久久久免费印度| 天天干天天舔天天操| 欧美日本成人| 亚洲欧美精品一区二区| 成人免费无码大片a毛片| 2020最新国产精品| 日韩精品专区在线影院观看| 一级片免费在线观看视频| 日韩一级特黄| 欧美一区日本一区韩国一区| 最新免费av网址| 国产高清视频一区二区| 欧美精选一区二区| 91aaa精品| 亚洲网一区二区三区| 精品国产乱码久久久久久夜甘婷婷| 手机看片国产精品| 99国产精品免费网站| 欧美精品一区在线观看| 中文在线观看免费视频| 欧美一级全黄| 亚洲人成网在线播放| 欧美黄色激情视频| 久久社区一区| 欧美日韩国产成人在线观看| 精品午夜福利视频| 国产精品日韩欧美一区| 日韩女优在线播放| 一区精品在线观看| 国产黑丝在线一区二区三区| 成人在线看片| 免费在线黄色网址| 中文字幕av在线一区二区三区| 一区二区免费在线观看| 国产黄a三级三级三级av在线看| 亚洲免费av观看| 夜夜添无码一区二区三区| 自拍一区在线观看| 欧美日韩美少妇| 熟女人妻一区二区三区免费看| 国产精品网址| 中文日韩电影网站| 午夜写真片福利电影网| 在线综合亚洲| 91精品国产综合久久香蕉的用户体验 | 91精品久久久久久综合五月天| 日韩二区三区在线| 日韩一级片在线免费观看| 欧美国产高潮xxxx1819| 欧美在线视频一区| 99久久久无码国产精品免费| 不卡区在线中文字幕| 日韩久久久久久久| 动漫一区二区| 欧美日韩中文精品| 久久久久亚洲AV成人无码国产| 成人直播大秀| 97久久久久久| 国产女人18毛片水18精| 97久久超碰精品国产| 伊甸园精品99久久久久久| 97人澡人人添人人爽欧美| 欧美三级日本三级少妇99| 亚洲欧美高清在线| 色中色综合网| 日本欧美在线视频| 国产91绿帽单男绿奴| 国产精品美女久久久久久久久久久 | 神马久久久久久久久久久| 国产一区欧美| 国产情人节一区| 可以直接在线观看的av| 亚洲国产精品一区二区久久恐怖片| 超碰在线公开97| 亚洲小说图片视频| 97精品久久久| 亚洲福利在线观看视频| 综合久久综合久久| 无限资源日本好片| 国产亚洲一区二区三区啪| 97精品国产97久久久久久| 性一交一乱一伧老太| 国产精品久久精品日日| 日本999视频| 亚欧洲精品视频在线观看| 九九热视频这里只有精品| 一级片免费观看视频| 国产三区在线成人av| www.99热这里只有精品| 成人知道污网站| 欧美日韩成人免费| 国产精品无码一区二区桃花视频 | 印度午夜性春猛xxx交| 美日韩一区二区| 四虎影视永久免费在线观看一区二区三区| 国产高清自产拍av在线| 亚洲国产精品成人va在线观看| 538精品在线观看| 国产精品亚洲专一区二区三区 | 91porn在线视频| 久久se精品一区精品二区| 日韩在线国产| 国产精品高潮久久| 最新日韩中文字幕| 伊人网av在线| 最新中文字幕一区二区三区| 亚洲av无日韩毛片久久| 在线成人直播| 99影视tv| 黄频免费在线观看| 亚洲女人被黑人巨大进入| 在线能看的av| 国产婷婷色一区二区三区四区| 欧美成人精品欧美一级乱| 一区二区小说| 国产精品高精视频免费| 丝袜美腿美女被狂躁在线观看| 欧美日韩精品三区| 91ts人妖另类精品系列| 国产精品中文欧美| www插插插无码视频网站| 久久a爱视频| 青青草成人在线| yiren22综合网成人| 欧美日本国产一区| 可以直接看的黄色网址| 从欧美一区二区三区| 国产白丝袜美女久久久久| 亚洲综合福利| 91人成网站www| 91资源在线观看| 国产一区二区三区免费视频| 一区二区三区免费观看视频| 亚洲乱码中文字幕综合| 日本性生活一级片| 日本亚洲三级在线| 成人高清dvd| 亚洲丝袜美腿一区| 成人欧美一区二区三区在线 | 婷婷久久一区| 国产伦精品一区二区三区| 婷婷综合六月| 色综合五月天导航| 国产天堂素人系列在线视频| 4438x亚洲最大成人网| 国产成人自拍视频在线| 国产精品女主播在线观看| 色悠悠在线视频| 蜜桃精品在线观看| 国产原创中文在线观看| 999视频精品| 久久综合福利| 欧美大片91| 国产精品久久久久久搜索| 在线看福利影| 国产亚洲一级高清| 欧美性猛交 xxxx| 欧美日韩一区二区欧美激情| 懂色av.com| 亚洲日本丝袜连裤袜办公室| 国产精品无码一区二区三区免费| 精品一区二区在线观看| 亚洲中文字幕无码中文字| 亚洲精品国产首次亮相| 欧美高清性xxxxhdvideosex| 日韩成人在线观看视频| 国产精品永久免费| 密臀av在线播放| 欧美第一淫aaasss性| 亚洲xxxxxx| 亚洲日本aⅴ片在线观看香蕉| 亚洲精品久久久蜜桃动漫| 欧美久久婷婷综合色| 自拍偷拍校园春色| 狠狠色狠狠色综合日日五| 婷婷在线精品视频| 国产精品成人一区二区艾草| 中国毛片在线观看| 99精品桃花视频在线观看| 日韩高清一二三区| 国产一区免费电影| 15—17女人毛片| 日韩高清一区二区| 国产深夜男女无套内射| 好看的av在线不卡观看| 日本丰满大乳奶| 一个色综合网| 制服诱惑一区| 国产精品99一区二区三| 亚洲高清123| 精品视频日韩| 亚洲激情一区二区| 日韩理论片av| 亚洲国产欧美一区二区三区不卡| 亚洲人成亚洲精品| 欧美日韩亚洲综合一区二区三区激情在线| 超碰地址久久| 国产一区二区免费在线观看| 超碰精品在线观看| 国产精品免费观看高清| 国产精品色呦| 久久国产一区二区| 亚欧日韩另类中文欧美| 欧美精品一区二区三区四区五区| 人妖一区二区三区| 欧美大香线蕉线伊人久久国产精品| 激情av综合| 久久久久国产精品视频| 亚洲精品**不卡在线播he| 欧美精品久久久| 欧美手机在线| 欧美aaa在线观看| 午夜日韩在线| 成人午夜免费在线| 香蕉成人久久| 黄色三级视频在线| 国内成+人亚洲+欧美+综合在线| 亚洲一二三av| 成人看片黄a免费看在线| 俄罗斯黄色录像| 成人精品电影在线观看| 国产二级一片内射视频播放| 91网站最新网址| 欧美一区二区三区粗大| 中文字幕日韩一区| 国产一级理论片| 黑人精品xxx一区| 伊人网综合在线| 欧美第一区第二区| 男女视频在线观看免费| 日韩在线中文视频| 久久亚洲导航| 国产aaa精品| 成人免费91| 国产在线视频欧美一区二区三区| 国产一区二区三区91| 天天成人综合网| 亚洲日本久久| 国产一级做a爰片久久| 国产在线精品一区二区不卡了| 国模无码视频一区| 欧美韩国日本一区| 久久久久香蕉视频| 91久久国产综合久久| 精品国产无码一区二区三区| 国产视频精品一区二区三区| 日本a级在线| 欧美影院在线播放| 久久久精品区| 日韩欧美三级一区二区| 国产精品v一区二区三区| 精品无码一区二区三区在线| 久久成人综合网| www.日本高清| 亚洲欧美一区二区三区国产精品 | 9.1片黄在线观看| 亚洲一区二区三区四区在线| 中国黄色一级视频| 亚洲精品久久久久久久久| 免费在线观看av片| 国产91色在线|| 国产精品qvod| 国产精品久久成人免费观看| 肉色丝袜一区二区| 午夜视频在线观看国产| 亚洲欧洲成人精品av97| 免费的毛片视频| 欧美精品一区二区久久久| 三区四区电影在线观看| 欧洲一区二区视频| 加勒比视频一区| 国产亚洲精品久久久久久久| 日本麻豆一区二区三区视频| 丰满少妇在线观看资源站| 亚洲午夜在线观看视频在线| 91亚洲国产成人久久精品麻豆| 亚洲精品视频网上网址在线观看| 成人免费一区二区三区牛牛| 成人在线视频福利| 久久久影院免费| 欧美午夜性生活| 久久久99精品免费观看不卡| 日韩成人av毛片| 日韩美女视频一区二区在线观看| 免费观看成人高潮| 成人黄色av播放免费| 成人免费a**址| 无人在线观看的免费高清视频| 91在线国产福利| 日韩精品一区二区av| 精品国偷自产国产一区| 超碰caoporn久久| 亚洲自拍偷拍区| 亚洲成人免费| 亚洲成人手机在线观看| 亚洲色大成网站www久久九九| 伊人久久亚洲综合| 日韩性生活视频| 日日夜夜亚洲| 亚洲第一页在线视频| 精品一区二区精品| 小早川怜子一区二区的演员表| 欧美无砖砖区免费| 日本在线免费网| 91亚洲精品一区二区| 影音先锋日韩在线| 色婷婷狠狠18禁久久| 亚洲精品va在线观看| 丰满人妻一区二区三区无码av| 欧美大片大片在线播放| 久久精品色播| 欧美牲交a欧美牲交aⅴ免费下载| 久久婷婷久久一区二区三区| 亚洲欧美综合自拍| 在线国产精品播放| 日本一区二区中文字幕| 色哟哟免费网站| 成人sese在线| 无码人妻av一区二区三区波多野| 国产一区二区三区在线播放免费观看| 欧美va在线观看| 欧美少妇一级片| 懂色av中文字幕一区二区三区 | 羞羞的视频在线观看| 成人av影视在线| 蜜桃av综合| 国产又粗又猛又爽又黄的视频小说| 51精品视频一区二区三区| wwwwxxxx在线观看| 欧美视频观看一区| 国产综合色视频| 日韩欧美不卡视频| 亚洲香蕉在线观看| 国产精区一区二区| 丰满少妇久久久| 欧美国产综合色视频| 精品国产av一区二区三区| 91av在线国产| 婷婷综合激情| 亚洲av无码一区二区三区观看| 欧美在线看片a免费观看| 91cn在线观看| 欧美激情国产日韩| 国模大尺度一区二区三区| 欧美一级视频免费观看| 久久精品人人做人人爽| 欧美三级午夜理伦三级在线观看 | 红杏一区二区三区| 色婷婷综合久久久久中文字幕| 18成人在线视频| 牛牛影视精品影视| 亚洲tv在线观看| 日韩在线观看一区二区| 好吊色视频在线观看| 国产亚洲精品美女| 99re8这里有精品热视频免费| www.久久久精品| 欧美日韩午夜激情|