程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java concurrency集合之CopyOnWriteArraySet_動力節點Java學院整理

Java concurrency集合之CopyOnWriteArraySet_動力節點Java學院整理

編輯:關於JAVA

CopyOnWriteArraySet介紹

它是線程安全的無序的集合,可以將它理解成線程安全的HashSet。有意思的是,CopyOnWriteArraySet和HashSet雖然都繼承於共同的父類AbstractSet;但是,HashSet是通過“散列表(HashMap)”實現的,而CopyOnWriteArraySet則是通過“動態數組(CopyOnWriteArrayList)”實現的,並不是散列表。

和CopyOnWriteArrayList類似,CopyOnWriteArraySet具有以下特性:

1. 它最適合於具有以下特征的應用程序:Set 大小通常保持很小,只讀操作遠多於可變操作,需要在遍歷期間防止線程間的沖突。

2. 它是線程安全的。

3. 因為通常需要復制整個基礎數組,所以可變操作(add()、set() 和 remove() 等等)的開銷很大。

4. 迭代器支持hasNext(), next()等不可變操作,但不支持可變 remove()等 操作。

5. 使用迭代器進行遍歷的速度很快,並且不會與其他線程發生沖突。在構造迭代器時,迭代器依賴於不變的數組快照。

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是通過CopyOnWriteArrayList實現的,它的API基本上都是通過調用CopyOnWriteArrayList的API來實現的。相信對CopyOnWriteArrayList了解的話,對CopyOnWriteArraySet的了解是水到渠成的事;所以,這裡就不再對CopyOnWriteArraySet的代碼進行詳細的解析了。 

CopyOnWriteArraySet示例

下面,我們通過一個例子去對比HashSet和CopyOnWriteArraySet。

import java.util.*;
import java.util.concurrent.*;

/*
 *  CopyOnWriteArraySet是“線程安全”的集合,而HashSet是非線程安全的。
 *
 *  下面是“多個線程同時操作並且遍歷集合set”的示例
 *  (01) 當set是CopyOnWriteArraySet對象時,程序能正常運行。
 *  (02) 當set是HashSet對象時,程序會產生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進行操作!
    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. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved