欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java concurrency集合之CopyOnWriteArraySet_动力节点Java学院整理

程序员文章站 2023-12-19 16:59:40
copyonwritearrayset介绍 它是线程安全的无序的集合,可以将它理解成线程安全的hashset。有意思的是,copyonwritearrayset和ha...

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的数据结构,如下图所示:

Java concurrency集合之CopyOnWriteArraySet_动力节点Java学院整理

说明:

  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异常。

上一篇:

下一篇: