而保证 set 的不重复性质的关键,显然就在于 CopyOnWriteArrayList 的 addIfAbsent 方法,我们还是点进 CopyOnWriteArrayList 源码看一看这个方法的实现:
其中的 indexOfRange 方法:
可以看到,也是加了 Monitor 锁来进行的,整个过程是这样的:
获取本来的 set ,是一个数组,以快照形式返回当前的数组;
indexOfRange 方法通过遍历查找查找元素出现位置,addIfAbsent方法完成不存在则加入,如果前一个为 false 后一个就不会执行;
加锁;
current 再次获取一次当前的快照,因为有可能第一次判断的过程有了其他线程的插入或者修改操作,此时已经不像等,就进入分支进行判断是否存在;
否则就要加入这个元素,和 CopyOnWriteArrayList 添加元素的最后操作是一样的;
解锁。
总结一下就是,线程安全的 Set 集合完全利用了 CopyOnWriteArrayList 集合的方法,对应的操作也是读写分别处理,写时复制的策略,通过 jvm 层面的锁来保证安全,那么保证不重复的方法就是遍历进行比较。
这样看来,相比于基于 HashMap 的去重方法,效率肯定会降低,不过如果基于线程安全的 HashMap ,插入操作从hash、比较、到考虑扩容各方面会因为加锁的过程更复杂,而对于一个不重复的 Set 来说,完全没必要,所以应该综合考虑之下采用了 List 为基础,暴力循环去重。
三、HashMap 的线程不安全
关于 HashMap 的相关问题,源码里已经分析过,大体是这样的。
不安全:
普通读写不一致问题;
死循环问题;
ConcurrentModificationException 异常。
解决:
util包的Hashtable集合线程安全;
用 synchronizedMap(new HashMap())包装;
使用 juc 包的 ConcurrentHashMap。
HashMap 和 ConcurrentHashMap 的源码分析: