HashMap
原理:JDK1.7底层是数组 + 链表,JDK1.8后底层是数组 + 红黑树
HashMap根据key的hash()方法计算哈希值,即数组的index
HashMap的默认容量是16,加载因子0.75,扩容默认2^n
为什么HashMap线程不安全
1、多线程并发put的时候导致数据不一致。
2、扩容的时候会调用 resize() 方法,这里的并发操作容易在一个桶上形成环形链表;这样当获取一个不存在的 key 时,计算出的 index 正好是环形链表的下标就会出现死循环。
HashTable
HashTable和HashMap的实现原理几乎一样,差别无非是
HashTable不允许key和value为null,HashMap允许key和value为空
HashTable是线程安全的
但是HashTable线程安全的策略实现代价却太大了,简单粗暴,get/put所有相关操作都是synchronized的,这相当于给整个哈希表加了一把大锁。
多线程访问时候,只要有一个线程访问或操作该对象,那其他线程只能阻塞,相当于将所有的操作串行化,在竞争激烈的并发场景中性能就会非常差。
ConcurrentHashMap
JDK1.7底层是数组 + 链表,JDK1.8后底层是数组 + 链表 + 红黑树
在JDK1.7中ConcurrentHashMap采用了数组+Segment+分段锁的方式实现,ConcurrentHashMap使用分段锁技术,将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问,能够实现真正的并发访问。
JDK8中彻底放弃了Segment转而采用的是Node,其设计思想也不再是JDK1.7中的分段锁思想,内部大量采用CAS操作。
CAS是compare and swap的缩写,即我们所说的比较交换。cas是一种基于锁的操作,而且是乐观锁。在java中锁分为乐观锁和悲观锁。悲观锁是将资源锁住,等一个之前获得锁的线程释放锁之后,下一个线程才可以访问。而乐观锁采取了一种宽泛的态度,通过某种方式不加锁来处理资源,比如通过给记录加version来获取数据,性能较悲观锁有很大的提高。
为什么ConcurrentHashMap 是线程安全的
因为ConcurrentHashMap的设计与实现非常精巧,大量的利用了volatile,final,CAS等加锁技术保证线程安全。