首页 > 技术文章 > 并发容器-CopyOnWriteSet

ningbing 2021-03-17 20:08 原文

CopyOnWriteSet
  该容器与CopyOnWriteArrayList相似,也是读取时不加锁,任意线程可以读。写入时加锁创建一个新的容器,然后写入新元素。
  内部用CopyOnWriteArrayList存储元素
private final CopyOnWriteArrayList<E> al;
1.判断元素是否存在
直接遍历al中的数组,查看是否有相等元素。
这里可以看出该容器是可以存放null的。

**
 * Returns {@code true} if this set contains the specified element.
 * More formally, returns {@code true} if and only if this set
 * contains an element {@code e} such that
 * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
 *  
 * @param o element whose presence in this set is to be tested
 * @return {@code true} if this set contains the specified element
 */
public boolean contains(Object o) {
    return al.contains(o);
}

-------------CopyOnWriteArrayList---------------------
/**
 * Returns {@code true} if this list contains the specified element.
 * More formally, returns {@code true} if and only if this list contains
 * at least one element {@code e} such that
 * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
 *
 * @param o element whose presence in this list is to be tested
 * @return {@code true} if this list contains the specified element
 */
public boolean contains(Object o) {
    Object[] elements = getArray();
    return indexOf(o, elements, 0, elements.length) >= 0;
}
2.增加元素

 1 public boolean add(E e) {
 2     return al.addIfAbsent(e);
 3 }
 4 
 5 -------------CopyOnWriteArrayList---------------------
 6 public boolean addIfAbsent(E e) {
 7     Object[] snapshot = getArray();
 8     // 查找元素是否已经在数组中存在,如果存在则直接返回false,否则调用addIfAbsent(e, snapshot)
 9     return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
10         addIfAbsent(e, snapshot);
11 }
12 /**
13  * A version of addIfAbsent using the strong hint that given
14  * recent snapshot does not contain e.
15  */
16 private boolean addIfAbsent(E e, Object[] snapshot) {
17     final ReentrantLock lock = this.lock;
18     lock.lock();
19     try {
20         Object[] current = getArray();
21         int len = current.length;
22         if (snapshot != current) {
23             // Optimize for lost race to another addXXX operation
24             int common = Math.min(snapshot.length, len);
25             for (int i = 0; i < common; i++)
26                 if (current[i] != snapshot[i] && eq(e, current[i])) // 这里是判断common长度范围内是否有元素与e相等
27                     return false;
28             if (indexOf(e, current, common, len) >= 0)  // 这里是判断common长度范围外是否有元素与e相等
29                     return false;
30         }
31         Object[] newElements = Arrays.copyOf(current, len + 1);
32         newElements[len] = e;
33         setArray(newElements);
34         return true;
35     } finally {
36         lock.unlock();
37     }
38 }
addIfAbsent(E e)方法内不直接lock然后进行判断。我的理解:是为了减少对性能的影响,对于已经set中已经存在该值的情况可以不加锁,只有不包含时才会进行同步。
 
3.删除元素
 1 public boolean remove(Object o) {
 2     return al.remove(o);
 3 }
 4 
 5 -------------CopyOnWriteArrayList---------------------
 6 public boolean remove(Object o) {
 7     Object[] snapshot = getArray();
 8     int index = indexOf(o, snapshot, 0, snapshot.length);
 9     return (index < 0) ? false : remove(o, snapshot, index);
10 }
11 private boolean remove(Object o, Object[] snapshot, int index) {
12     final ReentrantLock lock = this.lock;
13     lock.lock();
14     try {
15         Object[] current = getArray();
16         int len = current.length;
17         // 如果当前CopyOnWriteArrayList对象al已经发生变化,则再次寻找索引
18         if (snapshot != current) findIndex: {
19             int prefix = Math.min(index, len);
20             // 从[0,prefix-1]区间内找是否存在o,找到则break
21             for (int i = 0; i < prefix; i++) {
22                 if (current[i] != snapshot[i] && eq(o, current[i])) {
23                     index = i;
24                     break findIndex;
25                 }
26             }
27             // 如果在[0,prefix -1]区间内没找到且index>=len,表示index超出当前长度,返回false
28             if (index >= len)
29                 return false;
30             // 这部操作是有些时候对象al发生变化,但没影响对应index位置元素。这样可以省掉一次遍历操作。    
31             if (current[index] == o)
32                 break findIndex;
33             index = indexOf(o, current, index, len);
34             // 在[0,index-1],[index,index],[index,len-1]都没找到,返回false
35             if (index < 0)
36                 return false;
37         }
38         Object[] newElements = new Object[len - 1];
39         System.arraycopy(current, 0, newElements, 0, index);
40         System.arraycopy(current, index + 1,
41                          newElements, index,
42                          len - index - 1);
43         setArray(newElements);
44         return true;
45     } finally {
46         lock.unlock();
47     }
48 }
  这里是直接判断是否在al中存在,不存在返回false,存在则执行al的删除操作。当然删除时也是加锁,然后再次进行是否存在的判断,并进行删除的逻辑操作。
不在remove(Object o)处加锁,而是宁愿后续再检测list是否发生变化,也是基于性能考虑。使得对存在的元素进行remove操作时不用加锁。

 

推荐阅读