ConcurrentHashMap原始碼實現
concurrentHashmap和HashMap的區別:
concurrentHashmap和HashMap大多數下的使用場景基本一致,但最大的區別就是concurrentHashmap是執行緒安全的HashMap不是,在併發的場景下HashMap存在死迴圈的問題。可以參見部落格https://juejin.im/entry/5884f1a7128fe1006c3b6aac
concurrentHashmap和HashTable的區別:
HashTable是一個執行緒安全的容器類,在HashTable所有方法都是用synchronized關鍵字修飾的,也就是說它是執行緒安全的。但是HashTable的效能十分低下,對於每一個操作都要做加鎖操作,即使操作的是不同的bucket內的Entry也要全域性枷鎖,在高併發場景下效能低下。而concurrentHashmap引入了分段鎖的概念,對於不同Bucket的操作不需要全域性鎖來保證執行緒安全。
一、初始化及變數
對於不同的bucket用不同的鎖來管理,增加了併發性。
/**
* The default initial capacity for this table,
* used when not otherwise specified in a constructor.
* 預設的初始化容量
*/
static final int DEFAULT_INITIAL_CAPACITY = 16;
/**
* The default load factor for this table, used when not
* otherwise specified in a constructor.
* 裝載因子
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* The default concurrency level for this table, used when not
* otherwise specified in a constructor.
* 預設併發等級
*/
static final int DEFAULT_CONCURRENCY_LEVEL = 16;
/**
* The maximum capacity, used if a higher value is implicitly
* specified by either of the constructors with arguments. MUST
* be a power of two <= 1<<30 to ensure that entries are indexable
* using ints.
* 最大容量
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* 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.
* SEGMENT的最小容量
*/
static final int MIN_SEGMENT_TABLE_CAPACITY = 2;
/**
* The maximum number of segments to allow; used to bound
* constructor arguments. Must be power of two less than 1 << 24.SEGMENT的最大容量
*/
static final int MAX_SEGMENTS = 1 << 16; // slightly conservative
/**
* 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;
二、儲存結構
1 每一個segment都是一個特定的HashMap,segment的個數也就是併發數量,table的個數至少是2,而這個table中的每個元素才對應原來HashMap的桶。
2 概念
Segment : 可重入鎖,繼承ReentrantLock 也稱之為桶
HashEntry : 主要儲存鍵值對 可以叫節點
3 HashEntry原始碼
volatile保證了多執行緒每次讀的時候可見性。ConcurrentHashMap包含一個Segment陣列,每個Segment包含一個HashEntry陣列,當修改HashEntry陣列採用開鏈法處理衝突,所以它的每個HashEntry元素又是連結串列結構的元素。
static final class HashEntry<K,V> {
final int hash;
final K key;
volatile V value;
volatile HashEntry<K,V> next;
HashEntry(int hash, K key, V value, HashEntry<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
/**
* Sets next field with volatile write semantics. (See above
* about use of putOrderedObject.)
*/
final void setNext(HashEntry<K,V> n) {
UNSAFE.putOrderedObject(this, nextOffset, n);
}
// Unsafe mechanics
static final sun.misc.Unsafe UNSAFE;
static final long nextOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class k = HashEntry.class;
nextOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("next"));
} catch (Exception e) {
throw new Error(e);
}
}
}
四、構造方法
整個初始化是通過引數initialCapacity(初始容量),loadFactor(增長因子)和concurrencyLevel(併發等級)來初始化segmentShift(段偏移量)、segmentMask(段掩碼)和segment陣列。
public ConcurrentHashMap(int initialCapacity,
float loadFactor, int concurrencyLevel) {
if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
throw new IllegalArgumentException();
if (concurrencyLevel > MAX_SEGMENTS)
concurrencyLevel = MAX_SEGMENTS;//1 併發等級不能超過最大segments的數量
// Find power-of-two sizes best matching arguments
int sshift = 0;
int ssize = 1;
while (ssize < concurrencyLevel) {
++sshift;
ssize <<= 1;//2 如果你傳入的是15 就是向上取2的4次方倍 也就是16
}
this.segmentShift = 32 - sshift;//3 segmentShift和segmentMask在定位segment使用
this.segmentMask = ssize - 1;//4
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
int c = initialCapacity / ssize;
if (c * ssize < initialCapacity)
++c;
int cap = MIN_SEGMENT_TABLE_CAPACITY;
while (cap < c)
cap <<= 1;
//5 create segments and segments[0] 初始化segment
Segment<K,V> s0 =
new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
(HashEntry<K,V>[])new HashEntry[cap]);
Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];
UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
this.segments = ss;
}