Java中的ConcurrentHashMap集合

ConcurrentHashMap其采用分段锁设计,将一个大的线程安全的HashTable集合拆分成n多个小的HashTable集合,默认初始化16个小的HashTable集合。锁竞争概率非常小(即线程阻塞概率非常小)

JDK1.7的index需要计算两次。

ConcurrentHashMap是Java提供的线程安全的哈希表,它在多线程环境下提供高效的并发操作。它的底层实现原理是基于分段锁(Segment)和CAS(Compare and Swap)操作。

1. 分段锁(Segment):ConcurrentHashMap将整个存储空间分为多个Segment,每个Segment维护着一个子哈希表。每个Segment都拥有自己的锁,不同的线程可以独立地操作不同的Segment,从而实现了并发操作。每个Segment只锁定当前操作的部分,而不必锁定整个数据结构。

2. 哈希桶(Hash Bucket):每个Segment内部维护着一个哈希桶数组,用于存储键值对。每个哈希桶是一个链表或红黑树结构,用于解决哈希冲突。当链表长度超过阈值(默认为8)时,链表会转换为红黑树,提高查找和删除的效率。

3. CAS操作:ConcurrentHashMap使用CAS操作(无锁操作)来保证线程安全性。CAS是一种乐观锁的机制,它比传统的锁机制有更低的开销。CAS操作通过比较当前值和期望值是否相等来决定是否更新值,如果相等,则更新成功;如果不相等,则尝试重新读取和更新。CAS操作是原子的,当多个线程同时访问同一个Segment时,可以通过CAS来确保并发的安全性。

4. 扩容机制:ConcurrentHashMap在插入过程中会检查当前Segment的容量是否超过了阈值(默认为0.75)。如果超过了阈值,则会触发扩容操作。此时,会对整个ConcurrentHashMap进行扩容,并重新计算哈希值分布。实现过程中,只需要对每个Segment进行扩容操作,而不需要停止其他线程的访问。

通过分段锁和CAS操作的结合,ConcurrentHashMap实现了高效的并发操作。多个线程可以同时对不同的Segment进行访问,提高了并发性能。与HashTable相比,ConcurrentHashMap在并发性能、扩展性和灵活性方面有了显著的提升。

问题一:HashMap与HashTable的区别?

HashMap和HashTable都是常见的哈希表数据结构,用于在键值对之间建立映射关系。它们的区别如下:

1. 线程安全性:HashTable是线程安全的,而HashMap不是。HashTable的方法是同步的,可以在多线程环境下使用,但性能较差。HashMap则不提供线程安全的保证,可以在单线程环境下使用,性能较好。

2. NULL键和NULL值:HashMap允许使用null作为键和值,而HashTable不允许。如果尝试在HashTable中插入null键或值,将会抛出NullPointerException。

3. 继承关系:HashMap是AbstractMap类的子类,而HashTable是Dictionary类的子类。Dictionary是Java早期提供的一个已经被废弃的类。

4. 迭代器:HashMap的迭代器是fail-fast的,即在迭代过程中如果其他线程修改了HashMap的结构(增加、删除元素),将会抛出ConcurrentModificationException。HashTable不支持fail-fast机制。

5. 初始容量和扩容机制:HashMap初始容量默认为16,扩容时容量会自动翻倍。HashTable初始容量默认为11,扩容时容量会增加大约一倍加一,并重新计算哈希值分布。

综上所述,HashMap在性能、灵活性、扩展性方面优于HashTable,因此在单线程环境下推荐使用HashMap。如果需要在多线程环境下使用,可以考虑使用ConcurrentHashMap来替代HashTable。

问题二:HashTable的缺陷?

虽然HashTable在一些特定的场景中具有一些优势,但它也存在一些明显的缺陷:

1. 线程安全性开销:HashTable在实现上使用了synchronized关键字来保证线程安全性。这意味着在多线程环境下,对于非常频繁的读写操作,需要进行加锁和解锁操作,会导致性能下降。相对而言,其他线程安全的集合类(如ConcurrentHashMap)提供了更好的性能。

2. 低效的迭代器:HashTable的迭代器是通过Enumeration实现的,而不是Iterator。Enumeration的功能比较受限,只能进行遍历,不支持移除操作。同时,HashTable的迭代器不支持fail-fast机制,当在迭代过程中其他线程修改了HashTable的结构,可能会导致遍历失败或遍历到不一致的元素。

3. 存在数据冲突:HashTable使用内部哈希函数来将键映射为哈希值,然后通过哈希值将数据分散存储在不同的槽中。然而,由于哈希函数的限制,不同的键可能会产生相同的哈希值,这就是哈希冲突。HashTable使用开放地址法来解决冲突,但可能导致性能下降和数据分布不均匀。

4. 不支持null键和null值:HashTable不允许使用null作为键或值,如果尝试插入null键或值,会抛出NullPointerException。这在某些情况下可能会限制程序的灵活性和简洁性。

由于上述缺陷,Java推荐使用并发安全的HashMap的替代方案,如ConcurrentHashMap来满足多线程环境下的需求。

三:手写出ConcurrentHashMap

package lzx6;

import java.util.Hashtable;

public class MyConCurrentHashMap<K,V> {
    private Hashtable<K,V>[] hashtables;

    public MyConCurrentHashMap(){
        hashtables = new Hashtable[16];
        for (int i = 0; i < hashtables.length; i++) {
            hashtables[i] = new Hashtable<>();
        }
    }

    public void put(K k,V v){
        int hashTableIndex = k.hashCode()%hashtables.length;
        hashtables[hashTableIndex].put(k,v);
    }

    public V get(K k){
        int hashTableIndex = k.hashCode()%hashtables.length;
        return hashtables[hashTableIndex].get(k);
    }

    public static void main(String[] args) {
        MyConCurrentHashMap<String, String> myConCurrentHashMap = new MyConCurrentHashMap<>();
        myConCurrentHashMap.put("lzx","yy");
        myConCurrentHashMap.put("我爱你呀","yy");
        System.out.println(myConCurrentHashMap.get("我爱你呀"));
    }
}

运行结果:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jay/.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值