jdk 1.7
get()方法
- 首先获取到concurrentHashmap中segments数组的下标,找到对应的segment,然后找到对应的hashentrys数组,通过hash值计算获取下标,遍历hashentrys中指定下标的链表
- 从中判断是否存在hashentry满足输入的key的条件,如果有,则返回对应的value,如果没有,返回为空.
public V get(Object key) {
Segment<K,V> s; // manually integrate access methods to reduce overhead
HashEntry<K,V>[] tab;
int h = hash(key);
// 获取segments 数组的的下标
long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
// 获取segments 中指定下标的 segment 并获取到segment中的hashentry数组
if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
(tab = s.table) != null) {
// 遍历 hashEntry数组中指定下标的链表
for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile
(tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
e != null; e = e.next) {
K k;
if ((k = e.key) == key || (e.hash == h && key.equals(k)))
// 如果存在要查询的key,返回旧值
return e.value;
}
}
// 否则,返回为空
return null;
}
put()方法
- 首先判断输入的value的值是否为空,为空,抛出空指针异常
- 如果不为空,计算key的hash值,并计算出segments数组的下标,通过unsafe.getobject()方法尝试获取对应下标的segment,如果为空,则直接从segments数组中获取
- 获取到segment后,调用它的put方法进行存储
- segment的put方法中,首先尝试获取锁对象,如果获取成功,获取hashentrys数组,通过计算hash值,获取hashentry数组的下标,找到对应的链表,
- 遍历链表,首先判断遍历获取到的hashentry对象是是否为空,如果为空,说明没有与key符合的旧key,判断当前数组中的元素是否超过阈值,如果超过,进行扩容,如果没有超过,则新添加一个,并设置为链表的头部,并放在数组中.
- 如果链表不为空,则判断是否满足key和hash的条件,如果满足,则设置获取旧值,判断是否可以对旧值进行复制,如果可以,新值覆盖旧值,返回旧值,然后释放锁,返回旧值
segments数组不变,segment中的table数组会进行扩容,扩容后(oldseze * 2), 初始大小是16, key和value都不能为空
@SuppressWarnings("unchecked")
public V put(K key, V value) {
Segment<K,V> s;
// 判断 valu的值是否为空,如果为空,抛出空指针异常
if (value == null)
throw new NullPointerException();
// 计算key的hash值
int hash = hash(key);
// 通过hash值获取到segment的下标 segmentShift默认是4,segmentMask默认是15
int j = (hash >>> segmentShift) & segmentMask;
// 通过unsafe的getObject()方法获取为空时,从数组中获取对应的segment
if ((s = (Segment<K,V>)UNSAFE.getObject // nonvolatile; recheck
(segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment
s = ensureSegment(j);
// 调用segment的put方法存储key - value
return s.put(key, hash, value, false);
}
segment中的put方法
// segment中的 put方法
final V put(K key, int hash, V value, boolean onlyIfAbsent) {
// 对segment 尝试加锁,如果成果,node为null,如果失败node为scanAndLockForPut()的返回值
// scanAndLockForPut 先通过自旋的方式尝试获取锁,中间如果锁被释放,直接获取锁,如果获取的次数如果超过了最大自旋次数,变为等待锁
HashEntry<K,V> node = tryLock() ? null :
scanAndLockForPut(key, hash, value);
V oldValue;
try {
// 获取segment中的 数组信息
HashEntry<K,V>[] tab = table;
// 获取segment中 hashentry数组的下标
int index = (tab.length - 1) & hash;
// 通过数组和下标获取对应的hashentry对象(链表)
HashEntry<K,V> first = entryAt(tab, index);
// 遍历链表
for (HashEntry<K,V> e = first;;) {
// 判断获取到的链表是否为空
if (e != null) {
K k;
// 不为空,判断hash值和key值是否相同
if ((k = e.key) == key ||
(e.hash == hash && key.equals(k))) {
// 如果符合条件,获取旧值
oldValue = e.value;
// 判断是否允许修改旧值,如果同意,新值覆盖旧值
if (!onlyIfAbsent) {
e.value = value;
++modCount;
}
// 跳出循环
break;
}
e = e.next;
}
else {
// 如果 获取到的链表为空,说明对应的table下标数组内还没有添加元素, 把新添加的元素放在链表的头部
if (node != null)
node.setNext(first);
else
node = new HashEntry<K,V>(hash, key, value, first);
int c = count + 1;
// 判断当前元素的数量是否超过临界值,如果超过了,进行扩容
if (c > threshold && tab.length < MAXIMUM_CAPACITY)
rehash(node);
else
// 如果没超过,则添加到hashentry数组中
setEntryAt(tab, index, node);
++modCount;
count = c;
// 由于是新添加的,所有返回的value为空
oldValue = null;
// 跳出循环
break;
}
}
} finally {
// 释放锁
unlock();
}
// 返回旧值
return oldValue;
}