深刻Java集合學習系列:深刻CopyOnWriteArraySet

http://www.cnblogs.com/skywang12345/p/3498497.html?utm_source=tuicoolhtml


概要

本章是JUC系列中的CopyOnWriteArraySet篇。接下來,會先對CopyOnWriteArraySet進行基本介紹,而後再說明它的原理,接着經過代碼去分析,最後經過示例更進一步的瞭解CopyOnWriteArraySet。內容包括:
CopyOnWriteArraySet介紹
CopyOnWriteArraySet原理和數據結構
CopyOnWriteArraySet函數列表
CopyOnWriteArraySet源碼(JDK1.7.0_40版本)
CopyOnWriteArraySet示例java

轉載請註明出處:http://www.cnblogs.com/skywang12345/p/3498497.html數組

 

CopyOnWriteArraySet介紹

它是線程安全的無序的集合,能夠將它理解成線程安全的HashSet有意思的是,CopyOnWriteArraySet和HashSet雖然都繼承於共同的父類AbstractSet;可是,HashSet是經過「散列表(HashMap)」實現的,而CopyOnWriteArraySet則是經過「動態數組(CopyOnWriteArrayList)」實現的,並非散列表。
和CopyOnWriteArrayList相似,CopyOnWriteArraySet具備如下特性:
1. 它最適合於具備如下特徵的應用程序:Set 大小一般保持很小,只讀操做遠多於可變操做,須要在遍歷期間防止線程間的衝突。
2. 它是線程安全的。
3. 由於一般須要複製整個基礎數組,因此可變操做(add()、set() 和 remove() 等等)的開銷很大。
4. 迭代器支持hasNext(), next()等不可變操做,但不支持可變 remove()等 操做。
5. 使用迭代器進行遍歷的速度很快,而且不會與其餘線程發生衝突。在構造迭代器時,迭代器依賴於不變的數組快照。安全

建議:在學習CopyOnWriteArraySet以前,先經過"Java 集合系列16之 HashSet詳細介紹(源碼解析)和使用示例"對HashSet進行了解。數據結構

 

CopyOnWriteArraySet原理和數據結構

CopyOnWriteArraySet的數據結構,以下圖所示:多線程

說明
  1. CopyOnWriteArraySet繼承於AbstractSet,這就意味着它是一個集合。
  2. CopyOnWriteArraySet包含CopyOnWriteArrayList對象,它是經過CopyOnWriteArrayList實現的。而CopyOnWriteArrayList本質是個動態數組隊列,
因此CopyOnWriteArraySet至關於經過經過動態數組實現的「集合」! CopyOnWriteArrayList中容許有重複的元素;可是,CopyOnWriteArraySet是一個集合,因此它不能有重複集合。所以,CopyOnWriteArrayList額外提供了addIfAbsent()和addAllAbsent()這兩個添加元素的API,經過這些API來添加元素時,只有當元素不存在時才執行添加操做!
   至於CopyOnWriteArraySet的「線程安全」機制,和CopyOnWriteArrayList同樣,是經過volatile和互斥鎖來實現的。這個在前一章節介紹CopyOnWriteArrayList時數據結構時,已經進行了說明,這裏就再也不重複敘述了。框架

 

CopyOnWriteArraySet函數列表

複製代碼
// 建立一個空 set。
CopyOnWriteArraySet()
// 建立一個包含指定 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 中包含的元素上進行迭代的迭代器。
Iterator<E> iterator()
// 若是指定元素存在於此 set 中,則將其移除。
boolean remove(Object o)
// 移除此 set 中包含在指定 collection 中的全部元素。
boolean removeAll(Collection<?> c)
// 僅保留此 set 中那些包含在指定 collection 中的元素。
boolean retainAll(Collection<?> c)
// 返回此 set 中的元素數目。
int size()
// 返回一個包含此 set 全部元素的數組。
Object[] toArray()
// 返回一個包含此 set 全部元素的數組;返回數組的運行時類型是指定數組的類型。
<T> T[] toArray(T[] a)
複製代碼

 

CopyOnWriteArraySet源碼(JDK1.7.0_40版本)

CopyOnWriteArraySet.java的完整源碼以下:ide

  View Code

CopyOnWriteArraySet是經過CopyOnWriteArrayList實現的,它的API基本上都是經過調用CopyOnWriteArrayList的API來實現的。相信對CopyOnWriteArrayList瞭解的話,對CopyOnWriteArraySet的瞭解是水到渠成的事;因此,這裏就再也不對CopyOnWriteArraySet的代碼進行詳細的解析了。若對CopyOnWriteArrayList不瞭解,請參考「Java多線程系列--「JUC集合」02之 CopyOnWriteArrayList」。函數

 

CopyOnWriteArraySet示例

下面,咱們經過一個例子去對比HashSet和CopyOnWriteArraySet。學習

複製代碼
import java.util.*;
import java.util.concurrent.*;

/*
 *   CopyOnWriteArraySet是「線程安全」的集合,而HashSet是非線程安全的。
 *
 *   下面是「多個線程同時操做而且遍歷集合set」的示例
 *   (01) 當set是CopyOnWriteArraySet對象時,程序能正常運行。
 *   (02) 當set是HashSet對象時,程序會產生ConcurrentModificationException異常。
 *
 * @author skywang
 */
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進行操做!
        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();
            }
        }
    }
}
複製代碼

(某一次)運行結果

複製代碼
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, 
複製代碼

結果說明
因爲set是集合對象,所以它不會包含重複的元素。
若是將源碼中的set改爲HashSet對象時,程序會產生ConcurrentModificationException異常。

 


更多內容

1. Java多線程系列--「JUC集合」01之 框架

2. Java多線程系列--「JUC集合」02之 CopyOnWriteArrayLis

3. Java多線程系列目錄(共xx篇)

4. Java 集合系列目錄(Category)

相關文章
相關標籤/搜索