Java concurrency集合之CopyOnWriteArraySet_動力節(jié)點(diǎn)Java學(xué)院整理
CopyOnWriteArraySet介紹
它是線程安全的無序的集合,可以將它理解成線程安全的HashSet。有意思的是,CopyOnWriteArraySet和HashSet雖然都繼承于共同的父類AbstractSet;但是,HashSet是通過“散列表(HashMap)”實(shí)現(xiàn)的,而CopyOnWriteArraySet則是通過“動態(tài)數(shù)組(CopyOnWriteArrayList)”實(shí)現(xiàn)的,并不是散列表。
和CopyOnWriteArrayList類似,CopyOnWriteArraySet具有以下特性:
1. 它最適合于具有以下特征的應(yīng)用程序:Set 大小通常保持很小,只讀操作遠(yuǎn)多于可變操作,需要在遍歷期間防止線程間的沖突。
2. 它是線程安全的。
3. 因?yàn)橥ǔP枰獜?fù)制整個基礎(chǔ)數(shù)組,所以可變操作(add()、set() 和 remove() 等等)的開銷很大。
4. 迭代器支持hasNext(), next()等不可變操作,但不支持可變 remove()等 操作。
5. 使用迭代器進(jìn)行遍歷的速度很快,并且不會與其他線程發(fā)生沖突。在構(gòu)造迭代器時,迭代器依賴于不變的數(shù)組快照。
CopyOnWriteArraySet原理和數(shù)據(jù)結(jié)構(gòu)
CopyOnWriteArraySet的數(shù)據(jù)結(jié)構(gòu),如下圖所示:
說明:
1. CopyOnWriteArraySet繼承于AbstractSet,這就意味著它是一個集合。
2. CopyOnWriteArraySet包含CopyOnWriteArrayList對象,它是通過CopyOnWriteArrayList實(shí)現(xiàn)的。而CopyOnWriteArrayList本質(zhì)是個動態(tài)數(shù)組隊(duì)列,
所以CopyOnWriteArraySet相當(dāng)于通過通過動態(tài)數(shù)組實(shí)現(xiàn)的“集合”! CopyOnWriteArrayList中允許有重復(fù)的元素;但是,CopyOnWriteArraySet是一個集合,所以它不能有重復(fù)集合。因此,CopyOnWriteArrayList額外提供了addIfAbsent()和addAllAbsent()這兩個添加元素的API,通過這些API來添加元素時,只有當(dāng)元素不存在時才執(zhí)行添加操作!
至于CopyOnWriteArraySet的“線程安全”機(jī)制,和CopyOnWriteArrayList一樣,是通過volatile和互斥鎖來實(shí)現(xiàn)的。這個在前一章節(jié)介紹CopyOnWriteArrayList時數(shù)據(jù)結(jié)構(gòu)時,已經(jīng)進(jìn)行了說明,這里就不再重復(fù)敘述了。
CopyOnWriteArraySet函數(shù)列表
// 創(chuàng)建一個空 set。 CopyOnWriteArraySet() // 創(chuàng)建一個包含指定 collection 所有元素的 set。 CopyOnWriteArraySet(Collection<? extends E> c) // 如果指定元素并不存在于此 set 中,則添加它。 boolean add(E e) // 如果此 set 中沒有指定 collection 中的所有元素,則將它們都添加到此 set 中。 boolean addAll(Collection<? extends E> c) // 移除此 set 中的所有元素。 void clear() // 如果此 set 包含指定元素,則返回 true。 boolean contains(Object o) // 如果此 set 包含指定 collection 的所有元素,則返回 true。 boolean containsAll(Collection<?> c) // 比較指定對象與此 set 的相等性。 boolean equals(Object o) // 如果此 set 不包含任何元素,則返回 true。 boolean isEmpty() // 返回按照元素添加順序在此 set 中包含的元素上進(jìn)行迭代的迭代器。 Iterator<E> iterator() // 如果指定元素存在于此 set 中,則將其移除。 boolean remove(Object o) // 移除此 set 中包含在指定 collection 中的所有元素。 boolean removeAll(Collection<?> c) // 僅保留此 set 中那些包含在指定 collection 中的元素。 boolean retainAll(Collection<?> c) // 返回此 set 中的元素?cái)?shù)目。 int size() // 返回一個包含此 set 所有元素的數(shù)組。 Object[] toArray() // 返回一個包含此 set 所有元素的數(shù)組;返回?cái)?shù)組的運(yùn)行時類型是指定數(shù)組的類型。 <T> T[] toArray(T[] a)
CopyOnWriteArraySet是通過CopyOnWriteArrayList實(shí)現(xiàn)的,它的API基本上都是通過調(diào)用CopyOnWriteArrayList的API來實(shí)現(xiàn)的。相信對CopyOnWriteArrayList了解的話,對CopyOnWriteArraySet的了解是水到渠成的事;所以,這里就不再對CopyOnWriteArraySet的代碼進(jìn)行詳細(xì)的解析了。
CopyOnWriteArraySet示例
下面,我們通過一個例子去對比HashSet和CopyOnWriteArraySet。
import java.util.*; import java.util.concurrent.*; /* * CopyOnWriteArraySet是“線程安全”的集合,而HashSet是非線程安全的。 * * 下面是“多個線程同時操作并且遍歷集合set”的示例 * (01) 當(dāng)set是CopyOnWriteArraySet對象時,程序能正常運(yùn)行。 * (02) 當(dāng)set是HashSet對象時,程序會產(chǎn)生ConcurrentModificationException異常。 * * */ public class CopyOnWriteArraySetTest1 { // TODO: set是HashSet對象時,程序會出錯。 //private static Set<String> set = new HashSet<String>(); private static Set<String> set = new CopyOnWriteArraySet<String>(); public static void main(String[] args) { // 同時啟動兩個線程對set進(jìn)行操作! new MyThread("ta").start(); new MyThread("tb").start(); } private static void printAll() { String value = null; Iterator iter = set.iterator(); while(iter.hasNext()) { value = (String)iter.next(); System.out.print(value+", "); } System.out.println(); } private static class MyThread extends Thread { MyThread(String name) { super(name); } @Override public void run() { int i = 0; while (i++ < 10) { // “線程名” + "-" + "序號" String val = Thread.currentThread().getName() + "-" + (i%6); set.add(val); // 通過“Iterator”遍歷set。 printAll(); } } } }
(某一次)運(yùn)行結(jié)果:
ta-1, tb-1, ta-1, tb-1, ta-1, tb-1, ta-1, ta-2, tb-1, ta-1, ta-2, tb-1, tb-2, ta-2, ta-1, tb-2, tb-1, ta-3, ta-2, ta-1, tb-2, tb-1, ta-3, ta-2, tb-3, tb-2, ta-1, ta-3, tb-1, tb-3, ta-2, ta-4, tb-2, ta-1, ta-3, tb-1, tb-3, ta-2, ta-4, tb-2, tb-4, ta-3, ta-1, tb-3, tb-1, ta-4, ta-2, tb-4, tb-2, ta-5, ta-3, ta-1, tb-3, tb-1, ta-4, ta-2, tb-4, tb-2, ta-5, ta-3, tb-5, tb-3, ta-1, ta-4, tb-1, tb-4, ta-2, ta-5, tb-2, tb-5, ta-3, ta-0, tb-3, ta-1, ta-4, tb-1, tb-4, ta-2, ta-5, tb-2, tb-5, ta-3, ta-0, tb-3, tb-0, ta-4, ta-1, tb-4, tb-1, ta-5, ta-2, tb-5, tb-2, ta-0, ta-3, tb-0, tb-3, ta-1, ta-4, tb-1, tb-4, ta-2, ta-5, tb-5, ta-0, tb-0, ta-1, tb-2, tb-1, ta-3, ta-2, tb-3, tb-2, ta-4, ta-3, tb-4, tb-3, ta-5, ta-4, tb-5, tb-4, ta-0, ta-5, tb-0, tb-5, ta-1, ta-0, tb-1, tb-0, ta-2, ta-1, tb-2, tb-1, ta-3, ta-2, tb-3, tb-2, ta-4, ta-3, tb-4, tb-3, ta-5, tb-5, ta-0, tb-0, ta-4, ta-1, tb-4, tb-1, ta-5, ta-2, tb-5, tb-2, ta-0, ta-3, tb-0, tb-3, ta-1, ta-4, tb-1, tb-4, ta-2, ta-5, tb-2, tb-5, ta-3, ta-0, tb-3, tb-0, ta-4, tb-4, ta-5, tb-5, ta-0, tb-0,
結(jié)果說明:
由于set是集合對象,因此它不會包含重復(fù)的元素。
如果將源碼中的set改成HashSet對象時,程序會產(chǎn)生ConcurrentModificationException異常。
- java的各種集合為什么不安全(List、Set、Map)以及代替方案
- 簡單的理解java集合中的HashSet和HashTree幾個重寫方法
- 一分鐘了解Java中List集合與set集合的多種遍歷方式
- Java實(shí)現(xiàn)Redis的集合(set)命令操作
- Java HashSet集合存儲遍歷學(xué)生對象代碼實(shí)例
- Java底層基于鏈表實(shí)現(xiàn)集合和映射--集合Set操作詳解
- Java基于二分搜索樹、鏈表的實(shí)現(xiàn)的集合Set復(fù)雜度分析實(shí)例詳解
- Java底層基于二叉搜索樹實(shí)現(xiàn)集合和映射/集合Set功能詳解
- Java中的Set集合簡單匯總解析
- Java集合基礎(chǔ)知識 List/Set/Map詳解
- Java使用entrySet方法獲取Map集合中的元素
- java集合類源碼分析之Set詳解
- Java基礎(chǔ)之集合Set詳解
相關(guān)文章
java.lang.NullPointerException出現(xiàn)的幾種原因及解決方案
這篇文章主要介紹了java.lang.NullPointerException出現(xiàn)的幾種原因及解決方案,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05使用socket實(shí)現(xiàn)網(wǎng)絡(luò)聊天室和私聊功能
這篇文章主要介紹了使用socket實(shí)現(xiàn)網(wǎng)絡(luò)聊天室和私聊功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12Java之SpringCloud Eurka注冊錯誤解決方案
這篇文章主要介紹了Java之SpringCloud Eurka注冊錯誤解決方案,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07MyBatis JdbcType 與Oracle、MySql數(shù)據(jù)類型對應(yīng)關(guān)系說明
這篇文章主要介紹了MyBatis JdbcType 與Oracle、MySql數(shù)據(jù)類型對應(yīng)關(guān)系說明,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09spring注解之@Valid和@Validated的區(qū)分總結(jié)
@Validated和@Valid在基本驗(yàn)證功能上沒有太多區(qū)別,但在分組、注解地方、嵌套驗(yàn)證等功能上有所不同,下面這篇文章主要給大家介紹了關(guān)于spring注解之@Valid和@Validated區(qū)分的相關(guān)資料,需要的朋友可以參考下2022-03-03SpringBoot從配置文件中獲取屬性的四種方法總結(jié)
這篇文章主要介紹了SpringBoot從配置文件中獲取屬性的四種方法總結(jié),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02SpringAOP中基于注解實(shí)現(xiàn)通用日志打印方法詳解
這篇文章主要介紹了SpringAOP中基于注解實(shí)現(xiàn)通用日志打印方法詳解,在日常開發(fā)中,項(xiàng)目里日志是必不可少的,一般有業(yè)務(wù)日志,數(shù)據(jù)庫日志,異常日志等,主要用于幫助程序猿后期排查一些生產(chǎn)中的bug,需要的朋友可以參考下2023-12-12基于Java中對域和靜態(tài)方法的訪問不具有多態(tài)性(實(shí)例講解)
下面小編就為大家?guī)硪黄贘ava中對域和靜態(tài)方法的訪問不具有多態(tài)性(實(shí)例講解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10出現(xiàn)java.util.ConcurrentModificationException 問題及解決辦法
這篇文章主要介紹了出現(xiàn)java.util.ConcurrentModificationException 問題及解決辦法的相關(guān)資料,需要的朋友可以參考下2017-02-02