成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

并發容器(Map、List、Set)實戰及其原理分析—CopyOnWriteArrayList篇

開發 前端
CopyOnWriteArrayList 是 Java 中的一種線程安全的 List,它是一個可變的數組,支持并發讀和寫。它通過在修改操作時創建底層數組的副本來實現線程安全,從而保證了并發訪問的一致性。

1. JUC包下的并發容器

Java的集合容器框架中,主要有四大類別:List、Set、Queue、Map,大家熟知的這些集合類ArrayList、LinkedList、HashMap這些容器都是非線程安全的。

所以,Java先提供了同步容器供用戶使用。同步容器可以簡單地理解為通過synchronized來實現同步的容器,比如Vector、Hashtable以及SynchronizedList等容器。這樣做的代價是削弱了并發性,當多個線程共同競爭容器級的鎖時,吞吐量就會降低。

因此為了解決同步容器的性能問題,所以才有了并發容器。java.util.concurrent包中提供了多種并發類容器:

圖片圖片

CopyOnWriteArrayList

對應的非并發容器:ArrayList

目標:代替Vector、synchronizedList

原理:利用高并發往往是讀多寫少的特性,對讀操作不加鎖,對寫操作,先復制一份新的集合,在新的集合上面修改,然后將新集合賦值給舊的引用,并通過volatile 保證其可見性,當然寫操作的鎖是必不可少的了。

CopyOnWriteArraySet

對應的非并發容器:HashSet

目標:代替synchronizedSet

原理:基于CopyOnWriteArrayList實現,其唯一的不同是在add時調用的是CopyOnWriteArrayList的addIfAbsent方法,其遍歷當前Object數組,如Object數組中已有了當前元素,則直接返回,如果沒有則放入Object數組的尾部,并返回。

ConcurrentHashMap

對應的非并發容器:HashMap

目標:代替Hashtable、synchronizedMap,支持復合操作

原理:JDK6中采用一種更加細粒度的加鎖機制Segment“分段鎖”,JDK8中采用CAS無鎖算法。

ConcurrentSkipListMap

對應的非并發容器:TreeMap

目標:代替synchronizedSortedMap(TreeMap)

原理:Skip list(跳表)是一種可以代替平衡樹的數據結構,默認是按照Key值升序的。

2. CopyOnWriteArrayList

CopyOnWriteArrayList 是 Java 中的一種線程安全的 List,它是一個可變的數組,支持并發讀和寫。它通過在修改操作時創建底層數組的副本來實現線程安全,從而保證了并發訪問的一致性。

圖片圖片

2.1 應用場景

CopyOnWriteArrayList 的應用場景主要有兩個方面:

  • 讀多寫少的場景

由于 CopyOnWriteArrayList 的讀操作不需要加鎖,因此它非常適合在讀多寫少的場景中使用。例如,一個讀取頻率比寫入頻率高得多的緩存,使用 CopyOnWriteArrayList 可以提高讀取性能,并減少鎖競爭的開銷。

  • 不需要實時更新的數據

由于 CopyOnWriteArrayList 讀取的數據可能不是最新的,因此它適合于不需要實時更新的數據。例如,在日志應用中,為了保證應用的性能,日志記錄的操作可能被緩沖,并不是實時寫入文件系統,而是在某個時刻批量寫入。這種情況下,使用 CopyOnWriteArrayList 可以避免多個線程之間的競爭,提高應用的性能。

2.2 CopyOnWriteArrayList使用

基本使用

和 ArrayList 在使用方式方面很類似。

// 創建一個 CopyOnWriteArrayList 對象
CopyOnWriteArrayList phaser = new CopyOnWriteArrayList();
// 新增
copyOnWriteArrayList.add(1);
// 設置(指定下標)
copyOnWriteArrayList.set(0, 2);
// 獲取(查詢)
copyOnWriteArrayList.get(0);
// 刪除
copyOnWriteArrayList.remove(0);
// 清空
copyOnWriteArrayList.clear();
// 是否為空
copyOnWriteArrayList.isEmpty();
// 是否包含
copyOnWriteArrayList.contains(1);
// 獲取元素個數
copyOnWriteArrayList.size();

IP 黑名單判定

當應用接入外部請求后,為了防范風險,一般會對請求做一些特征判定,如對請求 IP 是否合法的判定就是一種。IP 黑名單偶爾會被系統運維人員做更新

public class CopyOnWriteArrayListDemo {
    private static CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
    // 模擬初始化的黑名單數據
    static {
        copyOnWriteArrayList.add("ipAddr0");
        copyOnWriteArrayList.add("ipAddr1");
        copyOnWriteArrayList.add("ipAddr2");
    }
    public static void main(String[] args) throws InterruptedException {
        Runnable task = new Runnable() {
            public void run() {
                // 模擬接入用時
                try {
                    Thread.sleep(new Random().nextInt(5000));
                } catch (Exception e) {}
                String currentIP = "ipAddr" + new Random().nextInt(6);
                if (copyOnWriteArrayList.contains(currentIP)) {
                    System.out.println(Thread.currentThread().getName() + " IP " + currentIP + "命中黑名單,拒絕接入處理");
                    return;
                }
                System.out.println(Thread.currentThread().getName() + " IP " + currentIP + "接入處理...");
            }
        };
        new Thread(task, "請求1").start();
        new Thread(task, "請求2").start();
        new Thread(task, "請求3").start();
        new Thread(new Runnable() {
            public void run() {
                // 模擬用時
                try {
                    Thread.sleep(new Random().nextInt(2000));
                } catch (Exception e) {}
                String newBlackIP = "ipAddr3";
                copyOnWriteArrayList.add(newBlackIP);
                System.out.println(Thread.currentThread().getName() + " 添加了新的非法IP " + newBlackIP);
            }
        }, "IP黑名單更新").start();
        Thread.sleep(1000000);
    }
}

圖片圖片

2.3 原理

很多時候,我們的系統應對的都是讀多寫少的并發場景。CopyOnWriteArrayList容器允許并發讀,讀操作是無鎖的,性能較高。至于寫操作,比如向容器中添加一個元素,則首先將當前容器復制一份,然后在新副本上執行寫操作,結束之后再將原容器的引用指向新容器。

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

圖片圖片

從整體架構上來說,CopyOnWriteArrayList 數據結構和 ArrayList 是一致的,底層是個數組,只不過 CopyOnWriteArrayList 在對數組進行操作的時候,基本會分四步走:

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

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

private transient volatile Object[] array;

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

優點

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

缺點

  • 內存占用問題,畢竟每次執行寫操作都要將原容器拷貝一份。數據量大時,對內存壓力較大,可能會引起頻繁GC;
  • 無法保證實時性,因為CopyOnWrite的寫時復制機制,所以在進行寫操作的時候,內存里會同時駐扎兩個對象的內存,舊的對象和新寫入的對象(注意:在復制的時候只是復制容器里的引用,只是在寫的時候會創建新對象添加到新容器里,而舊容器的對象還在使用,所以有兩份對象內存)

2.4 擴展知識:迭代器的 fail-fast 與 fail-safe 機制

在 Java 中,迭代器(Iterator)在迭代的過程中,如果底層的集合被修改(添加或刪除元素),不同的迭代器對此的表現行為是不一樣的,可分為兩類:Fail-Fast(快速失敗)和 Fail-Safe(安全失敗)。

fail-fast 機制

fail-fast 機制是java集合(Collection)中的一種錯誤機制。當多個線程對同一個集合的內容進行操作時,就可能會產生 fail-fast 事件。例如:當某一個線程A通過 iterator 去遍歷某集合的過程中,若該集合的內容被其他線程所改變了;那么線程A訪問集合時,就會拋出ConcurrentModificationException異常,產生 fail-fast 事件。

在 java.util 包中的集合,如 ArrayList、HashMap 等,它們的迭代器默認都是采用 Fail-Fast 機制。

圖片圖片

fail-fast解決方案

  • 方案一:在遍歷過程中所有涉及到改變modCount 值的地方全部加上synchronized 或者直接使用 Collection#synchronizedList,這樣就可以解決問題,但是不推薦,因為增刪造成的同步鎖可能會阻塞遍歷操作。
  • 方案二:使用CopyOnWriteArrayList 替換 ArrayList,推薦使用該方案(即fail-safe)。

fail-safe機制

任何對集合結構的修改都會在一個復制的集合上進行,因此不會拋出ConcurrentModificationException。在 java.util.concurrent 包中的集合,如 CopyOnWriteArrayList、ConcurrentHashMap 等,它們的迭代器一般都是采用 Fail-Safe 機制。

缺點:

  • 采用 Fail-Safe 機制的集合類都是線程安全的,但是它們無法保證數據的實時一致性,它們只能保證數據的最終一致性。在迭代過程中,如果集合被修改了,可能讀取到的仍然是舊的數據。
  • Fail-Safe 機制還存在另外一個問題,就是內存占用。由于這類集合一般都是通過復制來實現讀寫分離的,因此它們會創建出更多的對象,導致占用更多的內存,甚至可能引起頻繁的垃圾回收,嚴重影響性能。
責任編輯:武曉燕 來源: Fox愛分享
相關推薦

2022-04-13 08:23:31

Golang并發

2024-05-29 08:49:45

2013-09-11 09:49:18

Java數組集合

2022-07-18 11:13:07

容器安全Docker

2020-12-16 10:54:52

編程ForkJoin框架

2022-11-09 09:01:08

并發編程線程池

2019-04-09 11:07:15

HTTPS加密安全

2015-11-03 09:24:12

Java讀寫鎖分析

2012-02-22 14:14:43

Java

2023-04-06 15:26:35

Java線程安全

2021-01-25 20:20:35

數據分析SparkHadoop

2023-07-03 09:59:00

并發編程并發容器

2020-04-28 22:12:30

Nginx正向代理反向代理

2013-11-01 10:43:35

日志分析Awstats實戰Apache

2013-10-31 16:17:45

日志分析Awstats實戰Nginx

2013-10-31 11:08:15

2021-01-13 15:24:29

數據分析工具HQL原理

2024-11-11 17:20:52

2025-06-10 10:15:00

Java容器并發

2011-04-21 16:43:54

BlockingQue
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 午夜精品久久久久久久久久久久久 | 久久在线视频 | 国产成人在线播放 | 超碰在线人 | 欧美精品区 | 国产免费黄网 | 国产无套一区二区三区久久 | 337p日本欧洲亚洲大胆精蜜臀 | 成人精品在线观看 | 欧美激情久久久 | 欧洲一区二区在线 | 日韩av成人在线观看 | 精品一区视频 | 久草免费在线视频 | 日韩aⅴ在线观看 | 欧美精三区欧美精三区 | 在线永久看片免费的视频 | 久久国产精品无码网站 | 国产精品久久久久久久久久三级 | 精国产品一区二区三区四季综 | 69av网 | 欧美午夜剧场 | 91精品国产91久久久久久吃药 | 中文字幕一区二区三区日韩精品 | 亚洲国产成人av好男人在线观看 | 在线视频 亚洲 | 午夜免费成人 | 亚洲欧美日韩中文在线 | 亚洲社区在线 | 国产电影一区二区 | 欧美aⅴ | 久久久国产一区 | 国产精品国产精品国产专区不蜜 | 欧美成人一区二免费视频软件 | 国内精品久久久久久久影视简单 | 99国产精品99久久久久久粉嫩 | 综合网视频| 成人高清视频在线观看 | 国产精品影视在线观看 | 日韩一区二区三区在线 | 精品国产一区探花在线观看 |