HashMap并发先会出现什么问题那?
JDK中有了HashTable为何还要有个ConcurrentHashMap?
首先我们都知道HashMap不是线程安全的,但在什么情况下出会出现并发问题那?
HashMap:
HashMap的数据结构:数组、链表、红黑树(jdk1.8),
HashMap的原理:
1.自动扩容
数组默认大小:16
当数组的存储比例达到负载因子临界点时(默认75%),数组将会自动扩容为原来的2倍
2.hash算法
所有的对象都有hashCode(使用key的)
HashMap中的key的计算公式=(key.hashCode)^(key>>> 16)
3.数组下标
数据下标 = hashCode%(16-1) = hashCode%16
4.Hash冲突
不同对象算出来的数据下标相同的时,就发送了hash冲突
当发生hash冲突时数据将会以链表的方式存储,当链表长度大于8的时候,将后面的数据存在红黑树中
由于HashMap 不是线程安全有可能在并发情况下发送以下问题:
1.数据丢失
如果多个线程同时put相同的key时,那么其中一个线程的数据将会丢失或get不到了
2.Map.size()与实际不合
多线程环境下put的HashMap会被“损坏”,其中会造成size与实际不符合
3.导致死循环
当HashMap在做动态扩容是,需要对整个Hash表里的无素都需要被重算一遍。这叫rehash,
并发环境下的rehash过程可能会带来循环链表,导致死循环致使线程挂掉。
参考:疫苗:JAVA HASHMAP的死循环
为了解决这些问题JDK提供了2个线程安全的HashMap,HashTable和ConcurrentHashMap
HashTable 底层使用了synchronized,对所有的方法增加了锁,就解决了并发问题,但同时降低了效率
为了解决效率问题,JDK又提供了一个既能保持线程安全,有可以解决效率问题的工具类ConcurrentHashMap
ConcurrentHashMap原理:
分段锁
HashTable容器在竞争激烈的并发环境下表现出效率低下的原因,是因为所有访问HashTable的线程都必须竞争同一把锁,那假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
简单的说就是把数组问题多个段,每个段各有一把锁。JDK1.8后则改为每个数组的元素一个锁