JAVA的都知道的集合容器。
关于集合,先上一张脑图吧。。
image.png
本篇源自于本人以前的《JAVA安全与并发》一文。因其文太长,所以剔出。
容器
同步容器
在List中ArrayList对应的同步类为Vector(不一定安全),在Map中HashMap对应的安全类为HashTable,其底层原理是所有的方法都是synchronized。
而Collections.synchronizedXXX
(List、Set、Map)等使线程安全的,其底层所有方法是synchronized该对象来实现的。效率太低,而且在使用时要注意多线程操作不同方法的并发影响。不建议使用。
注意:Vector、HashTable只是同步,而不一定线程安全!!!
如两个线程,一个访问remove,一个访问get,因为他是针对于方法级的,只是同一时间段只能有一个线程执行该方法。
HashTable继承Dictionary,HashMap继承自AbstractMap。
HashMap可以存null,HashTable不可以。
以下类基本都来自于J.U.C,即java.util.concurrent包。
并发容器
并发容器。
CopyOnWriteArrayList
采用新开辟空间,然后复制数组,加上锁,执行添加操作来执行的。读没有锁。
使用场景:读多写少。
特点:
- 线程安全,适合并发;
- 实时性差;
- 内存开销大;
- Iterator不支持任何可变操作(add,set,remove)。
CopyOnWriteArraySet
底层为CopyOnWriteArrayList。其特点等参考CopyOnWriteArrayList。
Iterator不支持remove操作。
ConcurrentSkipListSet
TreeSet的线程安全版。底层采用ConcurrentSkipListMap来实现。
特点:
- 支持自然排序;
- 无法使用null;
- 多线程操作add、remove等都是线程安全的;
注意:批量操作时,如addAll、removeAll等都不一定能保证原子性,因为其底层也是执行add等方法,只能保证每单次执行是线程安全的。
ConcurrentHashMap
HashMap的线程安全版。
适用场景:
特点:
- 不允许null;
- 线程安全(见下面)。
ConcurrentSkipListMap
TreeMap的线程安全版。底层采用SkipList(跳表)来实现。
适用场景:用户数据量越大,优势越明显。
特点:
- 存取时间和数据量无关;(数据量越大越有优势)
- 有序;
- 安全。
HashMap和ConcurrentHashMap的比较
在JDK8中,HashMap的数据结构为数组+链表/红黑树(大于8的节点时)。其resize中,关于元素的分布,没有像之前那样rehash,而是通过计算“此节点的hash和原容量”的位运算,为0,保持原位,否则分配到“原索引+原容量”位置。(一说是看位运算的高分位)
在JDK8之前,ConcurrentHashMap对bucketIndex进行了分段(Segment),且在分段上有锁的保护。在锁期间,别的线程可以对其他分段进行处理。
在JDK8中,ConcurrentHashMap取消了之前的分段锁,而是采用CAS+synchronized来进行处理。
JDK8之ConcurrentHashMap插入实现原理
采用Unsafe.getObjectVolatile来直接从主内存中获取(保证取新),若为空,说明该位置第一次插入,用CAS插入即可。
若不为空,则synchronized该对象,再次执行Unsafe.getObjectVolatile保证取新,若变了重头再来。
若未变,判断该对象的hash是否大于0,是,则找该节点,有则替换值,无则指定到next上。
若否,判断是否为树节点,是,则插入到树中。
其他情况不处理。
若达到要求,则树化节点。
最后扩容,采用CAS来执行。保证数组长度为2倍,容量为1.5倍。
参考:深入浅出ConcurrentHashMap1.8。
HashMap的扩容机制---resize()。
网友评论