当前位置: 移动技术网 > IT编程>开发语言>Java > Java concurrency集合之CopyOnWriteArraySet_动力节点Java学院整理

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

2019年07月22日  | 移动技术网IT编程  | 我要评论

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网