一、ConcurrentHashMap
ConcurrentHashMap
相当于是HashMap
的多线程版本,它的功能本质上和HashMap
没什么区别。因为HashMap
在并发操作的时候会出现各种问题,比如死循环问题、数据覆盖等问题。而这些问题,只要使用ConcurrentHashMap
就可以完美地解决。
二、JDK1.7
ConcurrentHashMap
在JDK 1.7
中使用的数组 加 链表的结构,其中数组分为两类,大树组Segment
和 小数组 HashEntry
,而加锁是通过给Segment
添加ReentrantLock
重入锁来保证线程安全的。
在 ConcurrentHashMap
中,数据被分为多个段,每一段都有一个独立的锁,这样多个线程可以同时修改不同段的数据,从而实现并发读写。
当一个线程要对 ConcurrentHashMap
中的某一段数据进行修改时,它需要先获得该段数据对应的锁,在修改完后再释放锁。这样,其他线程就无法修改这一段数据,从而保证了数据的完整性。
同时,ConcurrentHashMap
还使用了链表和树来存储数据,提高了查询效率。总的来说,ConcurrentHashMap
是一个高效、线程安全的数据结构。
三、JDK1.8
ConcurrentHashMap
在JDK1.8
中使用的是数组 加 链表 加 红黑树的方式实现,它是通过 CAS
或者 synchronized
来保证线程安全的,并且缩小了锁的粒度,查询性能也更高。
这个算法的基本思想就是比较当前内存中的变量值和你预期变量值是否相等,如果相等,则接受修改的值,否则拒绝你的而操作。因为当前线程中的值已经不是最新的值,你的修改很可能会覆盖掉其他线程修改的结果。
每次插入数据时判断在当前数组下标是否是第一次插入,是就通过
CAS
方式插入,然后判断f.hash是否=-1,是的话就说明其他线程正在进行扩容,当前线程也会参与扩容;
删除方法用了synchronized修饰,保证并发下移除元素安全
当数组长度到达64且链表长度大于8时,链表转为红黑树。
标题JDK1.8 中为什么使用内置锁 synchronized替换 可重入锁 ReentrantLock?
- 在 JDK1.6 中,对 synchronized 锁的实现引入了大量的优化,并且 synchronized 有多种锁状态,会从无锁->偏向锁 -> 轻量级锁 -> 重量级锁一步步转换。
- 减少内存开销 。假设使用可重入锁来获得同步支持,那么每个节点都需要通过继承 AQS来获得同步支持。但并不是每个节点都需要获得同步支持的,只有链表的头节点(红黑树的根节点)需要同步,这无疑带来了巨大内存浪费。
CAS(Compare-And-Swap):比较并交换
CAS
是通过一个原子操作,用预期值去和实际值做对比,如果实际值和预期相同,则做更新操作。
如果预期值和实际不同,我们就认为,其他线程更新了这个值,此时不做更新操作。
而且这整个流程是原子性的,所以只要实际值和预期值相同,就能保证这次更新不会被其他线程影响。
既然这里用了CAS操作去更新值,那么就存在两者情况。
-
实际值和预期值相同
相同时,直接将值插入,因为此时是线程安全的。好了,这时插入操作完成。使用break;跳出了乐观锁。循环结束。
-
实际值和预期值不同
不同时,不进行操作,因为此时这个值已经被其他线程修改过了,此时这轮操作就结束了,因为还被乐观锁锁住,进入第三轮循环。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/104934.html