看ConcurrentHashMap下几个属性:
/**
* The default concurrency level for this table, used when not
* otherwise specified in a constructor.
* 默认的分段锁个数
*/
static final int DEFAULT_CONCURRENCY_LEVEL = 16;
/**
* The minimum capacity for per-segment tables. Must be a power
* of two, at least two to avoid immediate resizing on next use
* after lazy construction. 每个分段锁,最小容量
*/
static final int MIN_SEGMENT_TABLE_CAPACITY = 2;
/**
* 尝试获取锁的次数
* Number of unsynchronized retries in size and containsValue
* methods before resorting to locking. This is used to avoid
* unbounded retries if tables undergo continuous modification
* which would make it impossible to obtain an accurate result.
*/
static final int RETRIES_BEFORE_LOCK = 2;
/**
* The segments, each of which is a specialized hash table.
* 分段锁数组
*/
final Segment<K,V>[] segments;
/**
* Segments are specialized versions of hash tables. This
* Segments 也是一个定制的hashtable
* subclasses from ReentrantLock opportunistically, just to
* 同时他也是 ReentrantLock的子类
* simplify some locking and avoid separate construction.
*/
static final class Segment<K,V> extends ReentrantLock implements Serializable {
.....
}
//以put为例分析
/**
* Maps the specified key to the specified value in this table.
* Neither the key nor the value can be null.
* key 和 value 都不能为null 否则抛异常
* <p> The value can be retrieved by calling the <tt>get</tt> method
* with a key that is equal to the original key.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with <tt>key</tt>, or
* <tt>null</tt> if there was no mapping for <tt>key</tt>
* @throws NullPointerException if the specified key or value is null
*/
@SuppressWarnings("unchecked")
public V put(K key, V value) {
Segment<K,V> s;
if (value == null)
throw new NullPointerException();
int hash = hash(key);//先通过key求hash,再获取当前hash在哪个分段锁内,这些全是位操作,比较烦,也能分析透,还有UNSAFE类的使用比较多。UNSAFE是高危操作类,但是高效,功能强大。
int j = (hash >>> segmentShift) & segmentMask;
if ((s = (Segment<K,V>)UNSAFE.getObject // nonvolatile; recheck
(segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment
s = ensureSegment(j);//没找到合适分段,调用ensureSegment() 看下面,
return s.put(key, hash, value, false);//调用的分段锁Segment的put方法
}
/**
* Returns the segment for the given index, creating it and
* recording in segment table (via CAS) if not already present.
* 返回给定的索引的分段,不存在就创建一个。
* @param k the index
* @return the segment
*/
@SuppressWarnings("unchecked")
private Segment<K,V> ensureSegment(int k) {
final Segment<K,V>[] ss = this.segments;
long u = (k << SSHIFT) + SBASE; // raw offset
Segment<K,V> seg;
if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) == null) {//通过索引没取到分段,创建分段
Segment<K,V> proto = ss[0]; // use segment 0 as prototype //总是以ss[0]为例 这点写死了
int cap = proto.table.length;
float lf = proto.loadFactor;
int threshold = (int)(cap * lf);
HashEntry<K,V>[] tab = (HashEntry<K,V>[])new HashEntry[cap];
if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))
== null) { // recheck//再检查一次,有没有合适的分段
Segment<K,V> s = new Segment<K,V>(lf, threshold, tab);//创建分段
while ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))
== null) { //再检查一次,有没有合适的分段 第三次检查,
if (UNSAFE.compareAndSwapObject(ss, u, null, seg = s))//最后用cas方法,把新建的分段放到分段数组中
break;
}
}
}
return seg;
}
// 关键看下Segment类
/**
* Segments are specialized versions of hash tables. This
* subclasses from ReentrantLock opportunistically, just to
* simplify some locking and avoid separate construction.
* 它是ReentrantLock的子类
*/
static final class Segment<K,V> extends ReentrantLock implements Serializable {}
//看下Segment的put方法
final V put(K key, int hash, V value, boolean onlyIfAbsent) {
HashEntry<K,V> node = tryLock() ? null :
scanAndLockForPut(key, hash, value);//先用tryLock()获取锁,如果成功node=null ,否则进入scanAndLockForPut(key, hash, value)方法,看下面scanAndLockForPut方法
V oldValue;
try {
HashEntry<K,V>[] tab = table;
int index = (tab.length - 1) & hash;
HashEntry<K,V> first = entryAt(tab, index);
for (HashEntry<K,V> e = first;;) {
if (e != null) {//key 的hash位置有值了
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);//扩容 *2的大小
else
setEntryAt(tab, index, node);。
++modCount;
count = c;
oldValue = null;
break;
}
}
} finally {
unlock();//释放锁
}
return oldValue;
}
/**
* Scans for a node containing given key while trying to
* acquire lock, creating and returning one if not found. Upon
* return, guarantees that lock is held. UNlike in most
* methods, calls to method equals are not screened: Since
* traversal speed doesn't matter, we might as well help warm
* up the associated code and accesses as well.
* 通过key找对应node.没有就创建一个。
* @return a new node if key not found, else null
*/
private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) {
HashEntry<K,V> first = entryForHash(this, hash);
HashEntry<K,V> e = first;
HashEntry<K,V> node = null;
int retries = -1; // negative while locating node
while (!tryLock()) {//没有获取锁,重试
HashEntry<K,V> f; // to recheck first below
if (retries < 0) {
if (e == null) {
if (node == null) // speculatively create node
node = new HashEntry<K,V>(hash, key, value, null);
retries = 0;
}
else if (key.equals(e.key))
retries = 0;
else
e = e.next;
}
else if (++retries > MAX_SCAN_RETRIES) {//大于最大尝试次数
lock();//等待,阻塞锁
break;
}
else if ((retries & 1) == 0 &&
(f = entryForHash(this, hash)) != first) {
e = first = f; // re-traverse if entry changed
retries = -1;
}
}
return node;
}
/** get 方法不加锁
* Returns the value to which the specified key is mapped,
* or {@code null} if this map contains no mapping for the key.
*
* <p>More formally, if this map contains a mapping from a key
* {@code k} to a value {@code v} such that {@code key.equals(k)},
* then this method returns {@code v}; otherwise it returns
* {@code null}. (There can be at most one such mapping.)
*
* @throws NullPointerException if the specified key is null
*/
public V get(Object key) {
Segment<K,V> s; // manually integrate access methods to reduce overhead
HashEntry<K,V>[] tab;
int h = hash(key);
long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != 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;
}