047:Jdk1.8HashMap红黑树与ConcurrentHashMap源码分析
1 HashMap8为什么会引入红黑树
hashMap8中,如果链表长度>8,并且数组长度>64情况下,会将整个链表转换为红黑树,否则只是对数组扩容;
hashMap扩容将原来的数组扩容成新的数组,如果链表长度<6,红黑树转换链表。
2 HashMap8链表转红黑树实现原理1
public class MyIndex {
@Override
public int hashCode() {
return 30;
}
}
public class Test003 {
public static void main(String[] args) {
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
for (int i = 1; i <= 8; i++) {
MyIndex myIndex = new MyIndex();
objectObjectHashMap.put(myIndex, i);
}
MyIndex myIndex = new MyIndex();
objectObjectHashMap.put(myIndex, 9);
System.out.println(objectObjectHashMap.get(myIndex)); //9
}
}
HashMap中存放8个数据存入第9个断点调试
HashMap中获取第9个数据断点调试
3 HashMap8链表转红黑树实现原理2
public class Test003 {
public static void main(String[] args) {
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
// 数组长度提高到64
for (int i = 1; i <= 64; i++) {
objectObjectHashMap.put(i, i);
}
for (int i = 1; i <= 8; i++) {
MyIndex myIndex = new MyIndex();
objectObjectHashMap.put(myIndex, i);
}
MyIndex myIndex = new MyIndex();
objectObjectHashMap.put(myIndex, 9);
objectObjectHashMap.get(myIndex); //9
}
}
红黑树转换断点调试:
HashMap8中为什么将单向链表转换双向链表+红黑树
链表长度>8 转换红黑树;链表的长度<6的情况下,红黑树转换链表
目的就是方便能够从红黑树转换成链表
数组扩容的时候,重新计算index值。如果新链表长度<6的情况下,不会使用红黑树存放,否则情况下采用红黑树。
4 HashTable与HashMap存在的区别
课程内容:
1.为什么要使用ConcurrentHashMap
2.ConcurrentHashMap分段锁实现原理
3.Jdk1.8与1.7实现ConcurrentHashMap区别
hashMap与hashTable区别:
hashTable线程安全,hashMap线程不安全;
hashTable不允许存放key为空,hashMap允许存放key为空;
hashTable存在哪些弊端?
hashTable底层采用synchronized解决线程安全问题,在多线程情况下只允许一个线程对put方法实现操作,多线程变成单线程在执行,效率低。
既想要线程安全又想要多线程情况提高效率,使用ConcurrentHashMap。
5 HashTable线程安全存在那些问题
hathTable多线程情况下执行put方法的时候,使用synchronized把整个table数组所有链表全部锁住,只有一个线程可以操作。
6 ConcurrentHashMap分段锁技术原理分析
Jdk1.7的ConcurrentHashMap采用分段锁技术
ConcurrentHashMap会默认分成16个不同的小的hashTable,然后再通过一些计算方式在多线程的情况下,让每个键值对到不同的hashTable存放,从而能够解决多线程的效率问题,也能保证线程安全的问题。
注意:理论上最多只有16个线程同时能够对ConcurrentHashMap实现操作,ConcurrentHashMap设置好了小的hashTable,后期是无法扩容的。
Jdk1.8的ConcurrentHashMap删除分段锁技术,改用CAS无锁机制+synchronized。
7 基于HashTable手写分段锁技术
public class MyConcurrentHashMap<K, V> {
private Hashtable<K, V>[] hashtables;
public MyConcurrentHashMap() {
// 默认分成16个hashtables
hashtables = new Hashtable[16];
}
public void put(K k, V v) {
// 1.获取hash值
int hash = hash(k);
// 2.计算index值
int index = hash & (hashtables.length - 1);
// 3.获取对应的hashtable
Hashtable hashtable = hashtables[index];
if (hashtable == null) {
// 多线程new可能有线程安全问题 解决方案:锁、cas、预热机制(提前创建)
hashtable = new Hashtable();
}
hashtable.put(k, v);
hashtables[index] = hashtable;
}
public V get(K k) {
// 1.获取hash值
int hash = hash(k);
// 2.计算index值
int index = hash & (hashtables.length - 1);
// 3.获取对应的hashtable
Hashtable hashtable = hashtables[index];
if (hashtable == null) {
return null;
}
return (V) hashtable.get(k);
}
private int hash(Object k) {
// hashSeed
return 0 ^ k.hashCode();
}
}
public class Test004 {
public static void main(String[] args) {
MyConcurrentHashMap concurrentHashMap = new MyConcurrentHashMap();
concurrentHashMap.put("mayikt","zhangsan");
System.out.println(concurrentHashMap.get("mayikt")); //zhangsan
}
}
8 Jdl1.7ConcurrentHashMap源码分析
Jdk1.7ConcurrentHashMap源码分析:
1.分成16个不同的Segment,每个Segment有自己独立的table,本质上就是hashTable;,底层继承ReentrantLock;
2.根据key计算index存放在Segment位置。
ConcurrentHashMap —> Segment(HashTable) —> HashEntry
9 Jdk1.8ConcurrentHashMap源码分析
Jdk1.7ConcurrentHashMap底层采用分段锁,也就是每个Segment都有自己独立的锁;
Jdk1.8ConcurrentHashMap底层采用Cas无锁机制+synchronized。
New Node采用cas乐观锁机制保障线程安全性问题;如果index产生冲突,使用synchronized上锁。节点维度上锁,锁的粒度相比jdk1.7更加精细。