Java中的CopyOnWriteArrayList詳解
引入
ArrayList單線程下是安全的 但是多線程下存在不安全的問題
多線程下是不安全的
例如: 我們開啟多個線程向arraylist添加數(shù)據(jù)
public class ListTest { public static void main(String[] args) { List<String> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { new Thread(()->{ list.add("a"); System.out.println(list); }).start(); } } }
報出異常
解決這個問題我們可以使用集合工具類創(chuàng)建線程安全的ArrayList
List<String> strings = Collections.synchronizedList(new ArrayList<String>()); for (int i = 0; i < 101; i++) { new Thread(()->{ strings.add("a"); System.out.println(strings); }).start(); } }
但加入synchronized此時效率較低
所以我們可以使用juc包中的 CopyOnWriterArrayList
CopyOnWriterArrayList
CopyOnWriteArrayList (拷貝寫數(shù)組) 是ArrayList線程安全的集合
其中所有可變操作(add、set等等)都是通過對底層數(shù)組進行一次新的復制來實現(xiàn)的。
適合場景:讀多、寫少的并發(fā)場景
cow思想
它不存在“擴容”的概念,每次寫操作(add 、remove)都要copy一個副本,在副本的基礎(chǔ)上修改后改變array引用,所以稱為“CopyOnWrite”,因此在寫操作是加鎖,并且對整個list的copy操作時相當耗時的,過多的寫操作不推薦使用該存儲結(jié)構(gòu)。
讀取的方法沒有加鎖 寫的時候才加鎖 所以性能 比 vector高
我們可以從源碼中看出
public E get(int index) { return get(getArray(), index); } final Object[] getArray() { return array; } private E get(Object[] a, int index) { return (E) a[index]; }
讀操作并沒有加鎖 這也是效率高的原因
add源碼
寫操作加入了 lock鎖
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(); } }
(注:新版本jdk1.8后改成了synchronized 因為新版本對synchronized進行了升級提升了效率)
使用示例:
public class ListTest { public static void main(String[] args) { List<String> list = new CopyOnWriteArrayList<>(); for (int i = 0; i < 101; i++) { new Thread(()->{ list.add("a"); System.out.println(list); }).start(); } } }
缺點:
內(nèi)存問題:CopyOnWrite的寫復制機制,在進行寫操作的時候,內(nèi)存里會同時駐存在兩個對象的內(nèi)存,舊的對象和新寫入的對象(注意:在復制的時候只是復制容器里的引用,只是在寫的時候會創(chuàng)建新對 象添加到新容器里,而舊容器的對象還在使用,所以有兩份對象內(nèi)存)。如果這些對象占用的內(nèi)存比較大,比如說100M左右,那么再寫入50M數(shù)據(jù)進去,內(nèi)存就會占用150M,那么這個時候很有可能造成頻繁的minor GC和major GC。
數(shù)據(jù)一致性問題:CopyOnWrite容器只能保證數(shù)據(jù)的最終一致性,不能保證數(shù)據(jù)的實時一致性。所以如果你希望寫入的的數(shù)據(jù),馬上能讀到,請不要使用CopyOnWrite容器。
到此這篇關(guān)于Java中的CopyOnWriteArrayList詳解的文章就介紹到這了,更多相關(guān)CopyOnWriteArrayList詳解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決Spring boot2.0+配置攔截器攔截靜態(tài)資源的問題
這篇文章主要介紹了解決Spring boot2.0+配置攔截器攔截靜態(tài)資源的問題,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2019-08-08Java中g(shù)etParameterTypes()方法的使用與原理分析
本文詳細介紹了Java中g(shù)etParameterTypes()方法的使用方式、工作原理及其在實際開發(fā)中的應用,該方法用于獲取方法的參數(shù)類型列表,并通過反射機制在運行時動態(tài)地獲取這些信息,感興趣的朋友跟隨小編一起看看吧2025-01-01Maven中怎么手動添加jar包到本地倉庫詳解(repository)
這篇文章主要給大家介紹了關(guān)于Maven中怎么手動添加jar包到本地倉庫的相關(guān)資料,文中通過圖文以及實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2023-04-04詳解Java中Checked Exception與Runtime Exception 的區(qū)別
這篇文章主要介紹了詳解Java中Checked Exception與Runtime Exception 的區(qū)別的相關(guān)資料,這里提供實例幫助大家學習理解這部分內(nèi)容,需要的朋友可以參考下2017-08-08詳解spring boot jpa整合QueryDSL來簡化復雜操作
這篇文章主要介紹了詳解spring boot jpa整合QueryDSL來簡化復雜操作,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-04-04Java線程通信中關(guān)于生產(chǎn)者與消費者案例分析
這篇文章主要介紹了Java線程通信中關(guān)于生產(chǎn)者與消費者案例,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09