Java之ConcurrentHashMap线程安全原理

Java之ConcurrentHashMap线程安全原理

引言

对于Java程序员来说,HashMap是他们用到最多的映射(键值对)数据结构,但是HashMap是线程不安全的,在多线程的环境下,建议使用线程安全的ConcurrentHashMap。

锁机制

在JDK1.7及其之前,ConcurrentHashMap采用的是分段锁,也就是先将ConcurrentHashMap分成一定数量的段Segment,构成一个Segment数组,数组中的元素才是HashMap的键值对Node结点。可以理解为维护了一个Segment数组,数组中的元素是HashMap。

如此,在加锁时只需要对每个Segment段进行加锁即可,无需对整个ConcurrentHashMap加锁,提高了并发效率。

image-20241201184619140

但是,对Segment段加锁,会导致统一段内的其它Node结点也无法被其它线程访问。

在JDK1.8及其之后,取消了分段机制,保留原有HashMap的数据结构,单独只对某一个Node结点进行加锁,不会影响其它结点,大大提高了锁粒度,提高了并发效率。

image-20241201185321379

在JDK1.8及其之后,使用synchronized关键字以及CAS自旋锁保证多线程安全性。

当我们使用put方法新增键值对时:

public V put(K key, V value) {
   
        return putVal(key, value, false);
    }

会调用putVal方法:

final V putVal(K key, V value, boolean onlyIfAbsent) {
   
        if (key == null || value == null) throw new NullPointerException();
        int hash = spread(key.hashCode());
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {
   
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
   
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            else {
   
                V oldVal = null;
                synchronized (f) {
   
                    if (tabAt(tab, i) == f) {
   
                        if (fh >= 0) {
   
                            binCount = 
ConcurrentHashMap线程安全的实现原理是基于分段锁的机制。它将数据分成多个Segment(段),每个Segment拥有自己的HashEntry数组作为哈希表。每个Segment都是一个独立的Hashtable,拥有自己的锁。这样可以使得多个线程同时访问不同的Segment,从而提高并发读写的效率。 在ConcurrentHashMap中,读操作不需要加锁,因此多个线程可以同时读取数据,不会产生阻塞。而写操作只需要锁定对应的Segment,而不是整个哈希表,这样可以减少锁竞争的概率,提高并发性能。这种分段锁的机制使得ConcurrentHashMap在多线程环境下能够同时进行读写操作,保证了线程安全性,并且避免了整个哈希表被锁住而导致的性能下降。 每个Segment内部使用了volatile修饰的HashEntry数组来存储键值对,这样可以保证对其的读写操作的可见性。而且在进行写操作时,只需要锁住对应的Segment,可以避免对整个哈希表的锁竞争,提高并发性能。 总结起来,ConcurrentHashMap通过将数据分成多个Segment,并给每个Segment加锁的方式来实现线程安全。这种分段锁的机制使得多个线程可以同时读取不同的Segment,并且写操作只需要锁定对应的Segment,从而提高了并发读写的效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [ConcurrentHashMap线程安全的实现原理](https://blog.csdn.net/qq_52324490/article/details/110394436)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Java并发编程笔记之ConcurrentHashMap原理探究.docx](https://download.csdn.net/download/weixin_44609920/85726929)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [concurrenthashmap实现原理](https://blog.csdn.net/Elroy1230/article/details/123681027)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值