CopyOnWriteArrayList實現原理及源碼分析

来源:http://www.cnblogs.com/chengxiao/archive/2017/05/21/6881974.html
-Advertisement-
Play Games

CopyOnWriteArrayList是Java併發包中提供的一個併發容器,它是個線程安全且讀操作無鎖的ArrayList,寫操作則通過創建底層數組的新副本來實現,是一種讀寫分離的併發策略,我們也可以稱這種容器為"寫時複製器",Java併發包中類似的容器還有CopyOnWriteSet。本文會對C ...


  CopyOnWriteArrayList是Java併發包中提供的一個併發容器,它是個線程安全且讀操作無鎖的ArrayList,寫操作則通過創建底層數組的新副本來實現,是一種讀寫分離的併發策略,我們也可以稱這種容器為"寫時複製器",Java併發包中類似的容器還有CopyOnWriteSet。本文會對CopyOnWriteArrayList的實現原理及源碼進行分析。

實現原理

  我們都知道,集合框架中的ArrayList是非線程安全的,Vector雖是線程安全的,但由於簡單粗暴的鎖同步機制,性能較差。而CopyOnWriteArrayList則提供了另一種不同的併發處理策略(當然是針對特定的併發場景)。

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

  優缺點分析

  瞭解了CopyOnWriteArrayList的實現原理,分析它的優缺點及使用場景就很容易了。

  優點:

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

  缺點:

  缺點也很明顯,一是記憶體占用問題,畢竟每次執行寫操作都要將原容器拷貝一份,數據量大時,對記憶體壓力較大,可能會引起頻繁GC;二是無法保證實時性,Vector對於讀寫操作均加鎖同步,可以保證讀和寫的強一致性。而CopyOnWriteArrayList由於其實現策略的原因,寫和讀分別作用在新老不同容器上,在寫操作執行過程中,讀不會阻塞但讀取到的卻是老容器的數據。

源碼分析

  基本原理瞭解了,CopyOnWriteArrayList的代碼實現看起來就很容易理解了。

public boolean add(E e) {
        //ReentrantLock加鎖,保證線程安全
        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();
        }
    }    

  添加的邏輯很簡單,先將原容器copy一份,然後在新副本上執行寫操作,之後再切換引用。當然此過程是要加鎖的。

  刪除操作

 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)
                //如果要刪除的是列表末端數據,拷貝前len-1個數據到新副本上,再切換引用
                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();
        }
    }

  刪除操作同理,將除要刪除元素之外的其他元素拷貝到新副本中,然後切換引用,將原容器引用指向新副本。同屬寫操作,需要加鎖。

  我們再來看看讀操作,CopyOnWriteArrayList的讀操作是不用加鎖的,性能很高。

public E get(int index) {
        return get(getArray(), index);
    }

  直接讀取即可,無需加鎖

 private E get(Object[] a, int index) {
        return (E) a[index];
    }

 總結

  本文對CopyOnWriteArrayList的實現原理和源碼進行了分析,並對CopyOnWriteArrayList的優缺點也進行了分析(Java併發包中還提供了CopyOnWriteSet,原理類似)。其實所謂併發容器的優缺點,無非是取決於我們在面對特定併發場景時,是否能做出相對合理的選擇和應用。也希望本文能幫助到有需要的童鞋,共勉。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 、 高級語言運行機制 高級語言按程式的執行方式分為編譯型和解釋型兩種。 java語言比較特殊,Java程式的執行必須經過先編譯後解釋的步驟。 1 編譯生成位元組碼,只面向JVM(.class) 2Jvm執行解釋 JVM:(Java virtual machine) java虛擬機負責解釋執行位元組碼文件 ...
  • 流迭代器 2017-05-21 17:05:51 流迭代器是標準模板庫STL中的,是類模板,流迭代器實例化之後即可以和任何接受對應迭代器的函數一起使用(可以將流看做一個容器,把數據存儲在一個連續的緩衝區中,具有迭代器的功能和類似使用)。 istream_iterator 和ostream_itera ...
  • 因為原文中延續了組合模式的代碼示例來講訪問者模式 所以這裡就合併一起來複習了。但主要還是講訪問者模式。顧名思義這個模式會有一個訪問者類(就像近期的熱播劇“人民的名義”中的檢查官,跑到到貪官家裡調查取證,查實後就定罪),被訪問者類調用訪問者類的時候會將自身傳遞給它使用。直接看代碼: //被訪問者基類 ...
  • 1.順序查找 在查找中我們一個一個順序的遍歷表中的所有鍵並使用equals()方法來查找匹配的鍵。 優點:對數組的結構沒有特定的要求,可以使用數組或者鏈表實現,演算法簡單。 缺點:當數組個數n較大時,效率低下。 時間複雜度:查找命中時,最大時間複雜度是O(n),最小時間複雜度是O(1),平均時間複雜度 ...
  • There is 4 method of the referenced method: Student class: StudentComparator class: Test:public class MethodReferenceTest { ...
  • The most crucial distinguish between functional interface invoking and traditional method invoking is that transforming behavious or params it is.Howe ...
  • PHP是什麼文件? PHP,一個嵌套的縮寫名稱,是英文“超級文本預處理語言”(PHP:Hypertext Preprocessor)的縮寫。PHP 是一種 HTML 內嵌式的語言,PHP與微軟的ASP頗有幾分相似,都是一種在伺服器端執行的“嵌入HTML文檔的腳本語言”,語言的風格有類似於C語言,現在 ...
  • 轉載地址:http://www.yihaomen.com/article/java/302.htm mybatis中文官方教程:http://www.mybatis.org/mybatis-3/zh/getting-started.html 觀後提煉小結: 1、介面中的方法名 selectUserB ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...