目錄
什麼是CopyOnWrite容器
CopyOnWriteArrayList的實作原理
CopyOnWrite的應用場景
CopyOnWrite的缺點
首頁 Java java教程 Java並發程式設計:並發容器CopyOnWriteArrayList的實作原理

Java並發程式設計:並發容器CopyOnWriteArrayList的實作原理

Jul 30, 2018 am 11:41 AM
多執行緒 並行

  Copy-On-Write簡稱COW,是一種用於程式設計中的最佳化策略。其基本想法是,從一開始大家都在共享同一個內容,當某個人想要修改這個內容的時候,才會真正把內容Copy出去形成一個新的內容然後再改,這是一種延時懶惰策略。從JDK1.5開始Java並發包裡提供了兩個使用CopyOnWrite機制實現的並發容器,它們是CopyOnWriteArrayList和CopyOnWriteArraySet。 CopyOnWrite容器非常有用,可以在非常多的並發場景中使用。

什麼是CopyOnWrite容器

  CopyOnWrite容器即寫時複製的容器。通俗的理解是當我們往一個容器添加元素的時候,不直接往當前容器添加,而是先將當前容器進行Copy,複製出一個新的容器,然後新的容器裡添加元素,添加完元素之後,再將原容器的引用指向新的容器。這樣做的好處是我們可以對CopyOnWrite容器進行並發的讀取,而不需要加鎖,因為當前容器不會添加任何元素。所以CopyOnWrite容器也是一種讀寫分離的思想,讀寫不同的容器。

CopyOnWriteArrayList的實作原理

  在使用CopyOnWriteArrayList之前,我們先閱讀其原始碼了解下它是如何實現的。以下程式碼是向CopyOnWriteArrayList中add方法的實作(向CopyOnWriteArrayList裡加入元素),可以發現在新增的時候是需要加鎖的,否則多執行緒寫的時候會Copy出N個副本出來。

#1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

/**

     * 將指定元素追加到此清單的末端。

#

     *

#

     * @param e 若要附加到此清單的元素

#

     * @return <tt>true</tt>; (由{@link Collection#add}指定)

     #*/

    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();

#

    }

    }

   讀的時候不需要加鎖,如果讀的時候有多個線程正在向CopyOnWriteArrayList添加數據,讀還是會讀到舊的數據,因為寫的時候不會鎖住舊的CopyOnWriteArrayList。

#1

2

3

public E get(int index) {

    return get(getArray(), index);

#

}

#

   JDK中並沒有提供CopyOnWriteMap,我們可以參考CopyOnWriteArrayList來實作一個,基本程式碼如下:

#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

import java.util.Collection;

#

import java.util.Map;

import java.util.Set;

 

public class CopyOnWriteMap<K, V> implements Map<K, V>, Cloneable {

1

#

    private volatile Map internalMap;

      public CopyOnWriteMap() {

        internalMap = 

new

 

HashMap();##HashMap<K, V>();

    

}

      public V put(K key, V value) {

#

          synchronized (

#this

##) {             

Map newMap = #new HashMap#

new HashMap#(internalMap)(internalMap);             

V val = newMap.put(key, value);

#             

#internalMap = newMap;

#             

#return

 

val;

        }

    }  

    public 

V get(Object key) {

        return internalMap.get(key);     }  

    

publicvoid putAll(Map<? extends #K, ? 

#extends

 V> newData) {         synchronized 

(

#this##) {

            Map newMap = #new

 

HashMap#new HashMap#(internalMap)

(internalMap);

            #newMap.putAll(newData);

            

#internalMap = newMap;
        ######}###### ######    ######}###### ######}###### ### ##########

   實作很簡單,只要了解了CopyOnWrite機制,我們可以實作各種CopyOnWrite容器,在不同的應用場景中使用。

CopyOnWrite的應用場景

  CopyOnWrite並發容器用於讀取多寫少的並發場景。例如白名單,黑名單,商品類目的訪問和更新場景,假如我們有一個搜尋網站,用戶在這個網站的搜尋框中,輸入關鍵字搜尋內容,但是某些關鍵字不允許被搜尋。這些不能被搜尋的關鍵字會被放在一個黑名單當中,黑名單每天晚上更新一次。當使用者搜尋時,會檢查目前關鍵字在不在黑名單當中,如果在,則提示不能搜尋。實作程式碼如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

package com.ifeve.book;

 

import java.util.Map;

 

import com.ifeve.book.forkjoin.CopyOnWriteMap;

#

 

/**

 * 黑名單服務

#

 *

 * @author fangtengfei

 *

 */

public class BlackListServiceImpl {

 

    private static CopyOnWriteMap<String, Boolean> blackListMap = new CopyOnWriteMap< , Boolean>(

#

            1000##);

 

    public static boolean isBlackList(String id) {#

        returnblackListMap.get(id) == #null #false##null 

true

;

    

}

      public static 

void

 addBlackList(String id) {

        

blackListMap.put(id, Boolean.TRUE);#

    

}

 

    

/**

     

* 大量新增黑名單

     

*#

     

* @param ids

     

#*/#     public static 

void

 addBlackList(Map<String,Boolean> ids) {

#################################################################################################################################################################### ######        ######blackListMap.putAll(ids);###### ######    ######}###### ### ### ######}###### ### ##########

   程式碼很簡單,但是使用CopyOnWriteMap需要注意兩件事:

  1. 減少擴容開銷。依實際需要,初始化CopyOnWriteMap的大小,避免寫入時CopyOnWriteMap擴充的開銷。

  2. 使用批次新增。因為每次添加,容器每次都會複製,所以減少添加次數,可以減少容器的複製次數。如使用上面程式碼裡的addBlackList方法。

CopyOnWrite的缺點

  CopyOnWrite容器有許多優點,但同時也存在兩個問題,就是記憶體佔用問題和資料一致性問題。所以在開發的時候要注意一下。

  記憶體佔用問題。因為CopyOnWrite的寫時複製機制,所以在進行寫入操作的時候,記憶體裡會同時駐紮兩個物件的內存,舊的物件和新寫入的物件(注意:在複製的時候只是複製容器裡的引用,只是在寫的時候會創建新物件添加到新容器裡,而舊容器的物件還在使用,所以有兩份物件記憶體)。如果這些物件佔用的記憶體比較大,比如說200M左右,那麼再寫入100M資料進去,記憶體就會佔用300M,那麼這個時候很有可能造成頻繁的Yong GC和Full GC。先前我們系統中使用了一個服務由於每晚使用CopyOnWrite機制更新大對象,造成了每晚15秒的Full GC,應用響應時間也隨之變長。

  針對記憶體佔用問題,可以透過壓縮容器中的元素的方法來減少大物件的記憶體消耗,比如,如果元素全是10進位的數字,可以考慮把它壓縮成36進位或64進制。或不使用CopyOnWrite容器,而使用其他的並發容器,如ConcurrentHashMap。

  資料一致性問題。 CopyOnWrite容器只能保證資料的最終一致性,無法保證資料的即時一致性。所以如果你希望寫入的數據,馬上能讀到,請不要使用CopyOnWrite容器。

相關文章:

Java並發程式設計:CountDownLatch、CyclicBarrier與Semaphore

##【JAVA並發程式實戰】鎖定順序死鎖

相關影片:

Java多執行緒與並發庫高階應用程式影片教學#

以上是Java並發程式設計:並發容器CopyOnWriteArrayList的實作原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1666
14
CakePHP 教程
1425
52
Laravel 教程
1325
25
PHP教程
1273
29
C# 教程
1252
24
C++ 函式異常與多執行緒:並發環境下的錯誤處理 C++ 函式異常與多執行緒:並發環境下的錯誤處理 May 04, 2024 pm 04:42 PM

C++中函數異常處理對於多執行緒環境特別重要,以確保執行緒安全性和資料完整性。透過try-catch語句,可以在出現異常時擷取和處理特定類型的異常,以防止程式崩潰或資料損壞。

PHP 多執行緒如何實作? PHP 多執行緒如何實作? May 06, 2024 pm 09:54 PM

PHP多執行緒是指在一個行程中同時執行多個任務,透過建立獨立運行的執行緒實作。 PHP中可以使用Pthreads擴充模擬多執行緒行為,安裝後可使用Thread類別建立和啟動執行緒。例如,處理大量資料時,可將資料分割為多個區塊,並建立對應數量的執行緒同時處理,提高效率。

並發和協程在Golang API設計中的應用 並發和協程在Golang API設計中的應用 May 07, 2024 pm 06:51 PM

並發和協程在GoAPI設計中可用於:高效能處理:同時處理多個請求以提高效能。非同步處理:使用協程非同步處理任務(例如傳送電子郵件),釋放主執行緒。流處理:使用協程高效處理資料流(例如資料庫讀取)。

C++中如何處理多執行緒中的共享資源? C++中如何處理多執行緒中的共享資源? Jun 03, 2024 am 10:28 AM

C++中使用互斥量(mutex)處理多執行緒共享資源:透過std::mutex建立互斥量。使用mtx.lock()取得互斥量,對共享資源進行排他存取。使用mtx.unlock()釋放互斥。

C++ 記憶體管理在多執行緒環境中的挑戰與應對措施? C++ 記憶體管理在多執行緒環境中的挑戰與應對措施? Jun 05, 2024 pm 01:08 PM

在多執行緒環境中,C++記憶體管理面臨以下挑戰:資料競爭、死鎖和記憶體洩漏。因應措施包括:1.使用同步機制,如互斥鎖和原子變數;2.使用無鎖資料結構;3.使用智慧指標;4.(可選)實現垃圾回收。

C++ 多執行緒程式測試的挑戰與策略 C++ 多執行緒程式測試的挑戰與策略 May 31, 2024 pm 06:34 PM

多執行緒程式測試面臨不可重複性、並發錯誤、死鎖和缺乏可視性等挑戰。策略包括:單元測試:針對每個執行緒編寫單元測試,驗證執行緒行為。多執行緒模擬:使用模擬框架在控制執行緒調度的情況下測試程式。資料競態偵測:使用工具尋找潛在的資料競態,如valgrind。調試:使用調試器(如gdb)檢查運行時程序狀態,找到資料競爭根源。

Java函數的並發和多執行緒中的原子類別如何使用? Java函數的並發和多執行緒中的原子類別如何使用? Apr 28, 2024 pm 04:12 PM

原子類是Java中的執行緒安全類,可提供不可中斷的操作,對於確保並發環境中資料的完整性至關重要。 Java提供了以下原子類別:AtomicIntegerAtomicLongAtomicReferenceAtomicBoolean這些類別提供了取得、設定和比較值等方法,確保操作是原子的,不會被執行緒打斷。原子類在處理共享資料和防止資料損壞時非常有用,例如維護共用計數器的並發存取。

C++ 多執行緒程式設計中調試和故障排除的技術 C++ 多執行緒程式設計中調試和故障排除的技術 Jun 03, 2024 pm 01:35 PM

C++多執行緒程式設計的除錯技巧包括:使用資料競爭分析器來偵測讀寫衝突,並使用同步機制(如互斥鎖)解決。使用線程調試工具檢測死鎖,並透過避免嵌套鎖和使用死鎖檢測機制來解決。使用數據競爭分析器檢測數據競爭,並透過將寫入操作移入關鍵段或使用原子操作來解決。使用效能分析工具測量上下文切換頻率,並透過減少執行緒數量、使用執行緒池和卸載任務來解決過高的開銷。

See all articles