如何线程安全的使用HashMap

为什么HashMap是线程不安全的?

1、讲讲一下HashMap的存储结构

transient Node<K,V>[] table;
static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;
}

可以看到HashMap内部存储使用了一个node数组(默认大小是16),而Node类包含一个Node类型的next变量,指向下一个node相当于一个链表,所有根据Hash值计算的key一样的的数据会存储到一个bucket(桶)中(一个链表中),如下图

注意:java8中如果hash值相同的key的数量大于容量(默认8)时,会使用平衡树代替链表。

2、讲讲HashMap的扩容机制

HashMap内部的node数组默认是16,假设有100万个元素,每个桶(node)中平均存储62500个元素,那么Map的remove,get,put等方法的效率也会下降,为了解决这个问题,hashMap提供了自动扩容机制,当bucket个数超过容量(默认16)*loadfactor(默认0.75)之后,会把数组大小扩展为2*16=32个,并重新计算每个元素在在新数组中的位置。

图来源http://coding-geek.com/how-does-a-hashmap-work-in-java/


从图中可以看出,在已知key的情况下,没扩容之前获取EntryE需要遍历5次,扩容之后获取EntryE需要2次。

3、讲讲为什么线程不安全

两个方法,两个并发线程同时put元素,如果根据key值计算的hash值一样,那么put的数据会被覆盖;如果两个线程同时检测到当前数组大小大于容量*loadactory之后,会同时扩容并重新计算元素的新位置,并更新数组,这样最后就会出现有一个线程的数据会丢失。HashMap在线程并发时会容易死循环,死循环不是发生在put时而发生在扩容时。

4、讲讲如何安全的使用HashMap

//Hashtable
Map<String, String> hashtable = new Hashtable<>();
//synchronizedMap
Map<String, String&g
线程安全HashMap通常使用锁或者并发数据结构来实现。以下是两种常见的实现方式: 1. 使用锁实现线程安全HashMap 在单线程环境下,HashMap是非常高效的数据结构。但是在多线程环境下,由于线程之间的竞争,可能会导致HashMap出现错误或者一致的状态。为了解决这个问题,可以使用锁来保证线程安全。常见的实现方式包括: - 使用synchronized关键字来保护HashMap的读写操作。这种方式比较简单,但是会导致性能下降,因为每个线程都需要获得锁才能进行读写操作。 - 使用ConcurrentHashMap代替HashMap。ConcurrentHashMap是Java并发包提供的高性能线程安全Map实现。它利用分段锁来实现线程安全,可以同时支持多个读操作和少量写操作,因此在高并发环境下性能比较好。 2. 使用并发数据结构实现线程安全HashMap 除了使用锁,还可以使用Java并发包提供的一些并发数据结构来实现线程安全HashMap。例如: - ConcurrentHashMap。这是Java并发包提供的高性能线程安全Map实现,它利用分段锁来实现线程安全,可以同时支持多个读操作和少量写操作,因此在高并发环境下性能比较好。 - ConcurrentSkipListMap。这是Java并发包提供的高性能线程安全有序Map实现,它使用跳表来实现数据结构,可以支持快速的查找、插入和删除操作,而且可以保证数据的有序性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值