并发容器-CopyOnWriteSet
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 ? e==null : 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 ? e==null : 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操作时不用加锁。