Java concurrency集合之CopyOnWriteArraySet_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
CopyOnWriteArraySet介紹
它是線程安全的無(wú)序的集合,可以將它理解成線程安全的HashSet。有意思的是,CopyOnWriteArraySet和HashSet雖然都繼承于共同的父類(lèi)AbstractSet;但是,HashSet是通過(guò)“散列表(HashMap)”實(shí)現(xiàn)的,而CopyOnWriteArraySet則是通過(guò)“動(dòng)態(tài)數(shù)組(CopyOnWriteArrayList)”實(shí)現(xiàn)的,并不是散列表。
和CopyOnWriteArrayList類(lèi)似,CopyOnWriteArraySet具有以下特性:
1. 它最適合于具有以下特征的應(yīng)用程序:Set 大小通常保持很小,只讀操作遠(yuǎn)多于可變操作,需要在遍歷期間防止線程間的沖突。
2. 它是線程安全的。
3. 因?yàn)橥ǔP枰獜?fù)制整個(gè)基礎(chǔ)數(shù)組,所以可變操作(add()、set() 和 remove() 等等)的開(kāi)銷(xiāo)很大。
4. 迭代器支持hasNext(), next()等不可變操作,但不支持可變 remove()等 操作。
5. 使用迭代器進(jìn)行遍歷的速度很快,并且不會(huì)與其他線程發(fā)生沖突。在構(gòu)造迭代器時(shí),迭代器依賴于不變的數(shù)組快照。
CopyOnWriteArraySet原理和數(shù)據(jù)結(jié)構(gòu)
CopyOnWriteArraySet的數(shù)據(jù)結(jié)構(gòu),如下圖所示:

說(shuō)明:
1. CopyOnWriteArraySet繼承于AbstractSet,這就意味著它是一個(gè)集合。
2. CopyOnWriteArraySet包含CopyOnWriteArrayList對(duì)象,它是通過(guò)CopyOnWriteArrayList實(shí)現(xiàn)的。而CopyOnWriteArrayList本質(zhì)是個(gè)動(dòng)態(tài)數(shù)組隊(duì)列,
所以CopyOnWriteArraySet相當(dāng)于通過(guò)通過(guò)動(dòng)態(tài)數(shù)組實(shí)現(xiàn)的“集合”! CopyOnWriteArrayList中允許有重復(fù)的元素;但是,CopyOnWriteArraySet是一個(gè)集合,所以它不能有重復(fù)集合。因此,CopyOnWriteArrayList額外提供了addIfAbsent()和addAllAbsent()這兩個(gè)添加元素的API,通過(guò)這些API來(lái)添加元素時(shí),只有當(dāng)元素不存在時(shí)才執(zhí)行添加操作!
至于CopyOnWriteArraySet的“線程安全”機(jī)制,和CopyOnWriteArrayList一樣,是通過(guò)volatile和互斥鎖來(lái)實(shí)現(xiàn)的。這個(gè)在前一章節(jié)介紹CopyOnWriteArrayList時(shí)數(shù)據(jù)結(jié)構(gòu)時(shí),已經(jīng)進(jìn)行了說(shuō)明,這里就不再重復(fù)敘述了。
CopyOnWriteArraySet函數(shù)列表
// 創(chuàng)建一個(gè)空 set。 CopyOnWriteArraySet() // 創(chuàng)建一個(gè)包含指定 collection 所有元素的 set。 CopyOnWriteArraySet(Collection<? extends E> c) // 如果指定元素并不存在于此 set 中,則添加它。 boolean add(E e) // 如果此 set 中沒(méi)有指定 collection 中的所有元素,則將它們都添加到此 set 中。 boolean addAll(Collection<? extends E> c) // 移除此 set 中的所有元素。 void clear() // 如果此 set 包含指定元素,則返回 true。 boolean contains(Object o) // 如果此 set 包含指定 collection 的所有元素,則返回 true。 boolean containsAll(Collection<?> c) // 比較指定對(duì)象與此 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() // 返回一個(gè)包含此 set 所有元素的數(shù)組。 Object[] toArray() // 返回一個(gè)包含此 set 所有元素的數(shù)組;返回?cái)?shù)組的運(yùn)行時(shí)類(lèi)型是指定數(shù)組的類(lèi)型。 <T> T[] toArray(T[] a)
CopyOnWriteArraySet是通過(guò)CopyOnWriteArrayList實(shí)現(xiàn)的,它的API基本上都是通過(guò)調(diào)用CopyOnWriteArrayList的API來(lái)實(shí)現(xiàn)的。相信對(duì)CopyOnWriteArrayList了解的話,對(duì)CopyOnWriteArraySet的了解是水到渠成的事;所以,這里就不再對(duì)CopyOnWriteArraySet的代碼進(jìn)行詳細(xì)的解析了。
CopyOnWriteArraySet示例
下面,我們通過(guò)一個(gè)例子去對(duì)比HashSet和CopyOnWriteArraySet。
import java.util.*;
import java.util.concurrent.*;
/*
* CopyOnWriteArraySet是“線程安全”的集合,而HashSet是非線程安全的。
*
* 下面是“多個(gè)線程同時(shí)操作并且遍歷集合set”的示例
* (01) 當(dāng)set是CopyOnWriteArraySet對(duì)象時(shí),程序能正常運(yùn)行。
* (02) 當(dāng)set是HashSet對(duì)象時(shí),程序會(huì)產(chǎn)生ConcurrentModificationException異常。
*
*
*/
public class CopyOnWriteArraySetTest1 {
// TODO: set是HashSet對(duì)象時(shí),程序會(huì)出錯(cuò)。
//private static Set<String> set = new HashSet<String>();
private static Set<String> set = new CopyOnWriteArraySet<String>();
public static void main(String[] args) {
// 同時(shí)啟動(dòng)兩個(gè)線程對(duì)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) {
// “線程名” + "-" + "序號(hào)"
String val = Thread.currentThread().getName() + "-" + (i%6);
set.add(val);
// 通過(guò)“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é)果說(shuō)明:
由于set是集合對(duì)象,因此它不會(huì)包含重復(fù)的元素。
如果將源碼中的set改成HashSet對(duì)象時(shí),程序會(huì)產(chǎn)生ConcurrentModificationException異常。
- java的各種集合為什么不安全(List、Set、Map)以及代替方案
- 簡(jiǎn)單的理解java集合中的HashSet和HashTree幾個(gè)重寫(xiě)方法
- 一分鐘了解Java中List集合與set集合的多種遍歷方式
- Java實(shí)現(xiàn)Redis的集合(set)命令操作
- Java HashSet集合存儲(chǔ)遍歷學(xué)生對(duì)象代碼實(shí)例
- Java底層基于鏈表實(shí)現(xiàn)集合和映射--集合Set操作詳解
- Java基于二分搜索樹(shù)、鏈表的實(shí)現(xiàn)的集合Set復(fù)雜度分析實(shí)例詳解
- Java底層基于二叉搜索樹(shù)實(shí)現(xiàn)集合和映射/集合Set功能詳解
- Java中的Set集合簡(jiǎn)單匯總解析
- Java集合基礎(chǔ)知識(shí) List/Set/Map詳解
- Java使用entrySet方法獲取Map集合中的元素
- java集合類(lèi)源碼分析之Set詳解
- Java基礎(chǔ)之集合Set詳解
相關(guān)文章
基于SpringBoot bootstrap.yml配置未生效的解決
這篇文章主要介紹了基于SpringBoot bootstrap.yml配置未生效的解決方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10
使用Java語(yǔ)言將XML轉(zhuǎn)為PDF的方法
這篇文章主要介紹了使用Java語(yǔ)言將XML轉(zhuǎn)為PDF的方法,本文將介紹通過(guò)Java代碼來(lái)實(shí)現(xiàn)該格式轉(zhuǎn)換的方法,需要的朋友可以參考下2022-03-03
SpringBoot結(jié)合Swagger2自動(dòng)生成api文檔的方法
這篇文章主要介紹了SpringBoot結(jié)合Swagger2自動(dòng)生成api文檔的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-05-05
Java ConcurrentHashMap鎖分段機(jī)制使用及代碼實(shí)例
ConcurrentHashMap是Java中的一種線程安全的哈希表,通過(guò)鎖分段機(jī)制提高了并發(fā)性能,在Java 8中,ConcurrentHashMap引入了CAS操作和更復(fù)雜的節(jié)點(diǎn)繼承結(jié)構(gòu),進(jìn)一步優(yōu)化了并發(fā)操作2025-01-01
深入了解Java中String、Char和Int之間的相互轉(zhuǎn)換
這篇文章主要介紹了深入了解Java中String、Char和Int之間的相互轉(zhuǎn)換,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下2019-06-06

