阻塞队列:多个线程操作的队列。
JDK7:分成若干哥Segments。
是一个key和value数组。
原理:https://blog.csdn.net/it_dx/article/details/77941538
我们看get的方法。
get:
public V get(Object key) {
Segment<K,V> s;
HashEntry<K,V>[] tab;
//根据key的值计算hash值
int h = hash(key);
//获得segment的index
long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
//保证拿到的是最新的
if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
//通过hash值定位segment中对应的HashEntry 遍历HashEntry,
如果key存在,返回key对应的value 如果不存在则返回null
(tab = s.table) != null) {
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)))
return e.value;
}
}
return null;
}
put:
public V put(K key, V value) {
Segment<K,V> s;
//键和值都不能为空
if (value == null)
throw new NullPointerException();
//计算key的hash值
int hash = hash(key);
//获得key所属的segemngt
int j = (hash >>> segmentShift) & segmentMask;
if ((s = (Segment<K,V>)UNSAFE.getObject
(segments, (j << SSHIFT) + SBASE)) == null)
//初试化segment(懒加载模式)
s = ensureSegment(j);
return s.put(key, hash, value, false);
}
final V put(K key, int hash, V value, boolean onlyIfAbsent) {
//获取锁,保证线程安全
HashEntry<K,V> node = tryLock() ? null :
scanAndLockForPut(key, hash, value);
V oldValue;
try {
HashEntry<K,V>[] tab = table;
int index = (tab.length - 1) & hash;
HashEntry<K,V> first = entryAt(tab, index); //定位到具体的HashEntry
for (HashEntry<K,V> e = first;;) { //3
if (e != null) {
K k;
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 {
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
setEntryAt(tab, index, node);
++modCount;
count = c;
oldValue = null;
break;
}
}
} finally {
//释放锁
unlock();
}
//返回旧值
return oldValue;
}
size:
public int size() {
final Segment<K,V>[] segments = this.segments;
int size;
boolean overflow;
long sum;
long last = 0L;
int retries = -1;
try {
for (;;) {
//RETRIES_BEFORE_LOCK为不变常量2 尝试两次不锁住Segment的方式来统计每个Segment的大小,如果在统计的过程中Segment的count发生变化,这时候再加锁统计Segment的count
if (retries++ == RETRIES_BEFORE_LOCK) { //加锁
for (int j = 0; j < segments.length; ++j)
ensureSegment(j).lock();
}
sum = 0L;
size = 0;
overflow = false;
for (int j = 0; j < segments.length; ++j) {
Segment<K,V> seg = segmentAt(segments, j);
if (seg != null) {
sum += seg.modCount; //2
int c = seg.count;
if (c < 0 || (size += c) < 0)
overflow = true;
}
}
if (sum == last)
break;
last = sum;
}
} finally {
if (retries > RETRIES_BEFORE_LOCK) {
for (int j = 0; j < segments.length; ++j)
segmentAt(segments, j).unlock();
}
}
return overflow ? Integer.MAX_VALUE : size;
}
我总结一个重要的知识点:
通过key的hash再位移值获得key所属的segment,segment.put中通过key的hash值再(tab.length - 1) & hash获取index定位到是哪个hashEntry。遍历hashEntry。
判断hashEntry是不是存在这个值:
1.通过key,1不成立的话就是2.节点的hash==key的hash&&节点的key去equals输入的key。
put的话就是如果通过key的hash再(tab.length - 1) & hash去定位HashEntry有值了,就加个链表节点,节点的key就是元素的hash值,并且节点的key要equals元素的key。
----------------------------------