ConcurrentHashMap:线程安全的hashMap
CopyOnWriteArrayList:线程安全的List
BlockingQueue:接口,阻塞队列,适合数据共享通道
ConcurrentLinkedQueue:高效的非阻塞并发队列,使用链表,可以看作线程安全的LinkedList
ConcurrentSkipListMap:跳表,快速查找
Vector 和hashtable
效率低,使用synchronized 修饰方法
HashMap和ArrayList
ConcurrentHashMap 和 CopyOnWriteArrayList
绝大多数并发情况下,二者的性能都更好
为什么hashMap是线程不安全的?
同时put碰撞导致数据丢失
同时put扩容导致数据丢失
死循环CPU100%
JDK1.7 ConcurrentHashMap实现和分析
最外层是多个segment,每个segment的底层与hashmap相似,仍然是数组和链表组成的拉链法。
每个segment上了ReentrantLock锁,每个segment之间互不影响,提高效率。
默认有16个segment,最多支持16个线程并发写,不可以扩容。
JDK1.8 ConcurrentHashMap实现和分析
数组+链表+红黑树 CAS+synchronized
putVal流程
- 判断 k,v 不为空
- 计算hash值
- 根据节点的类型来赋值,或者增长链表,或者给红黑树增加节点
- 满足阈值就红黑树化
- 返回oldVal
为什么要把1.7的结构改为1.8?
- 数据结构 原来的并发度为16,现在为数组的大小
- hash碰撞 原来链表的时间复杂度为O(N),现在先使用拉链法,后使用红黑树为O(log2N)
为什么要超过8转换为红黑树?
泊松分布,超过8的概率是非常小的
组合操作
replace
putIfAbsent
CopyOnWriteArrayList
代替Vector和SynchronizedList,他们的锁粒度太大,并发效率低。
使用场景
读操作需要尽可能的快,写操作可以慢一些。
读写规则
读取不需要加锁,写入不会阻塞读取操作,只有写入和写入之间需要进行同步等待。
在修改时将原来的数据进行拷贝再修改,最后将引用指向修改后的内容。原来的数据不做修改
缺点
数据过期 CopyOnWriteArrayList 只能保证最终的一致性,不能保证实时数据的一致性。迭代器的创建时间决定了能不能读到数据。如果希望数据马上读到,不要使用。
内存占用 在进行写操作时,内存中会存在2个对象的内存。