ConcurrentHashMap(一):常量,成員變數,靜態程式碼塊,內部類,spread函式,tabAt函式等詳解
雜湊表
Node節點(K-V)所放的位置:key的雜湊值 & 陣列.len-1
1.假如計算的下標為如圖5,就會來到連結串列結構中,從頭開始檢查,看是否有key一致的節點,如果一致就進行替換,如果沒有一致的節點就插入到末尾(要判斷是否樹化)
2.如果計算的下標為如圖的10,可以看見是TreeBin節點(代表當前桶位已經樹化成紅黑樹——可以提高查詢的效率),其中TreeBin節點中維護了兩個結構:紅黑樹結構和連結串列結構,先會插入到TreeNode連結串列結構,然後插入到TreeNode紅黑樹結構中(需要平衡操作)
3.如圖的FWD節點 (擴容的時候可以用) ,處理完一個桶位之後,引用需要指向FWD節點,其他執行緒來到雜湊表時:1.如果是put操作,到FWD節點會幫你一起擴容。2.如果是get操作(FWD中儲存了新表、擴容後表的引用)會到新表中執行查詢操作。
4.擴容的順序是從後往前遷移的——方便迭代的讀,就是從前往後讀的話,讀到後面失去的數可以到新表去讀(如果從低到高 容易衝突)
1_深入理解ConcurrentHashMap 常量
/* ---------------- Constants -------------- */
/**
* The largest possible table capacity. This value must be
* exactly 1<<30 to stay within Java array allocation and indexing
* bounds for power of two table sizes, and is further required
* because the top two bits of 32bit hash fields are used for
* control purposes.
* 雜湊表陣列最大限制
*/
private static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* The default initial table capacity. Must be a power of 2
* (i.e., at least 1) and at most MAXIMUM_CAPACITY.
* 雜湊表預設值
*/
private static final int DEFAULT_CAPACITY = 16;
/**
* The largest possible (non-power of two) array size.
* Needed by toArray and related methods.
*/
static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* The default concurrency level for this table. Unused but
* defined for compatibility with previous versions of this class.
* 併發級別,jdk1.7遺留下來的,1.8只有在初始化的時候用了一用。
* 不代表併發級別。
*/
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
/**
* The load factor for this table. Overrides of this value in
* constructors affect only the initial table capacity. The
* actual floating point value isn't normally used -- it is
* simpler to use expressions such as {@code n - (n >>> 2)} for
* the associated resizing threshold.
* 負載因子,JDK1.8中 ConcurrentHashMap 是固定值
*/
private static final float LOAD_FACTOR = 0.75f;
/**
* The bin count threshold for using a tree rather than list for a
* bin. Bins are converted to trees when adding an element to a
* bin with at least this many nodes. The value must be greater
* than 2, and should be at least 8 to mesh with assumptions in
* tree removal about conversion back to plain bins upon
* shrinkage.
* 樹化閾值,指定桶位 連結串列長度達到8的話,有可能發生樹化操作。
*/
static final int TREEIFY_THRESHOLD = 8;
/**
* The bin count threshold for untreeifying a (split) bin during a
* resize operation. Should be less than TREEIFY_THRESHOLD, and at
* most 6 to mesh with shrinkage detection under removal.
* 紅黑樹轉化為連結串列的閾值
*/
static final int UNTREEIFY_THRESHOLD = 6;
/**
* The smallest table capacity for which bins may be treeified.
* (Otherwise the table is resized if too many nodes in a bin.)
* The value should be at least 4 * TREEIFY_THRESHOLD to avoid
* conflicts between resizing and treeification thresholds.
* 聯合TREEIFY_THRESHOLD控制桶位是否樹化,只有當table陣列長度達到64且 某個桶位 中的連結串列長度達到8,才會真正樹化
*/
static final int MIN_TREEIFY_CAPACITY = 64;
/**
* Minimum number of rebinnings per transfer step. Ranges are
* subdivided to allow multiple resizer threads. This value
* serves as a lower bound to avoid resizers encountering
* excessive memory contention. The value should be at least
* DEFAULT_CAPACITY.
* 執行緒遷移資料最小步長,控制執行緒遷移任務最小區間一個值
*/
private static final int MIN_TRANSFER_STRIDE = 16;
/**
* The number of bits used for generation stamp in sizeCtl.
* Must be at least 6 for 32bit arrays.
* 擴容相關,計算擴容時生成的一個標識戳(雖然沒有用final修飾,但是全文沒有修改他,不改變)
* 不管什麼執行緒來 16擴容到32都是不變的
*/
private static int RESIZE_STAMP_BITS = 16;
/**
* The maximum number of threads that can help resize.
* Must fit in 32 - RESIZE_STAMP_BITS bits.
* 65535 表示併發擴容最多執行緒數
*/
private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
/**
* The bit shift for recording size stamp in sizeCtl.
* 擴容相關
*/
private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
/*
* Encodings for Node hash fields. See above for explanation.
*/
//node節點的hash為-1時,表示當前節點是FWD節點,已經遷移了
static final int MOVED = -1; // hash for forwarding nodes
//node節點的hash為-2時,表示當前節點已經樹化,
// 表示當前節點為TreeBin節點,TreeBin節點代理操作紅黑樹
static final int TREEBIN = -2; // hash for roots of trees
// ReservationNode的hash值
static final int RESERVED = -3; // hash for transient reservations
//0x7fffffff =》0111 1111 1111 1111 1111 1111 1111 1111
//可以將一個負數 位與運算後得到正數,但是不是絕對值
static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash
/** Number of CPUS, to place bounds on some sizings.
* 當前系統的cpu數量
*/
static final int NCPU = Runtime.getRuntime().availableProcessors();
/** For serialization compatibility.
* JDK1.8序列化 為了相容jdk1.7的ConcurrentHashMap儲存的
*/
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("segments", Segment[].class),
new ObjectStreamField("segmentMask", Integer.TYPE),
new ObjectStreamField("segmentShift", Integer.TYPE)
};
2_深入理解ConcurrentHashMap 成員變數
/* ---------------- Fields -------------- */
/**
* The array of bins. Lazily initialized upon first insertion.
* Size is always a power of two. Accessed directly by iterators.
* 雜湊表,長度一定是2次方數
*/
transient volatile Node<K,V>[] table;
/**
* The next table to use; non-null only while resizing.
* 擴容過程中,會將擴容中的新table 賦值給nextTable 保持引用,擴容結束之後,這裡會被設定為Null
*/
private transient volatile Node<K,V>[] nextTable;
/**
* Base counter value, used mainly when there is no contention,
* but also as a fallback during table initialization
* races. Updated via CAS.
* 化整為零
* LongAdder 中的 baseCount 未發生競爭時 或者 當前LongAdder處於加鎖狀態時,增量累到到baseCount中
*/
private transient volatile long baseCount;
/**
* Table initialization and resizing control. When negative, the
* table is being initialized or resized: -1 for initialization,
* else -(1 + the number of active resizing threads). Otherwise,
* when table is null, holds the initial table size to use upon
* creation, or 0 for default. After initialization, holds the
* next element count value upon which to resize the table.
* sizeCtl < 0
* 1. -1 表示當前table正在初始化(有執行緒在建立table陣列),當前執行緒需要自旋等待..
* 2.表示當前table陣列正在進行擴容 ,高16位表示:擴容的標識戳 低16位表示:(1 + nThread) 當前參與併發擴容的執行緒數量
*
* sizeCtl = 0,表示建立table陣列時 使用DEFAULT_CAPACITY為大小
*
* sizeCtl > 0
*
* 1. 如果table未初始化,表示初始化大小
* 2. 如果table已經初始化,表示下次擴容時的 觸發條件(閾值)
*/
private transient volatile int sizeCtl;
/**
* The next table index (plus one) to split while resizing.
* 擴容過程中,記錄當前進度。所有執行緒都需要從transferIndex中分配區間任務,去執行自己的任務。
*/
private transient volatile int transferIndex;
/**
* Spinlock (locked via CAS) used when resizing and/or creating CounterCells.
* LongAdder中的cellsBuzy 0表示當前LongAdder物件無鎖狀態,1表示當前LongAdder物件加鎖狀態)
* 只有一個物件能持有加鎖狀態
*
*/
private transient volatile int cellsBusy;
/**
* Table of counter cells. When non-null, size is a power of 2.
* LongAdder中的cells陣列,當baseCount發生競爭後,會建立cells陣列,
* 執行緒會通過計算hash值 取到 自己的cell ,將增量累加到指定cell中
* 總數 = sum(cells) + baseCount
*/
private transient volatile CounterCell[] counterCells;
// views
private transient KeySetView<K,V> keySet;
private transient ValuesView<K,V> values;
private transient EntrySetView<K,V> entrySet;
3_分解靜態程式碼塊
// Unsafe mechanics
private static final sun.misc.Unsafe U;
/**表示sizeCtl屬性在ConcurrentHashMap中記憶體偏移地址*/
private static final long SIZECTL;
/**表示transferIndex屬性在ConcurrentHashMap中記憶體偏移地址*/
private static final long TRANSFERINDEX;
/**表示baseCount屬性在ConcurrentHashMap中記憶體偏移地址*/
private static final long BASECOUNT;
/**表示cellsBusy屬性在ConcurrentHashMap中記憶體偏移地址*/
private static final long CELLSBUSY;
/**表示cellValue屬性在CounterCell中記憶體偏移地址*/
private static final long CELLVALUE;
/**表示陣列第一個元素的偏移地址*/
private static final long ABASE;
private static final int ASHIFT;
static {
try {
U = sun.misc.Unsafe.getUnsafe();
Class<?> k = ConcurrentHashMap.class;
SIZECTL = U.objectFieldOffset
(k.getDeclaredField("sizeCtl"));
TRANSFERINDEX = U.objectFieldOffset
(k.getDeclaredField("transferIndex"));
BASECOUNT = U.objectFieldOffset
(k.getDeclaredField("baseCount"));
CELLSBUSY = U.objectFieldOffset
(k.getDeclaredField("cellsBusy"));
Class<?> ck = CounterCell.class;
CELLVALUE = U.objectFieldOffset
(ck.getDeclaredField("value"));
Class<?> ak = Node[].class;
ABASE = U.arrayBaseOffset(ak);
//表示陣列單元所佔用空間大小,scale 表示Node[]陣列中每一個單元所佔用空間大小
int scale = U.arrayIndexScale(ak);
//1 0000 & 0 1111 = 0
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
//numberOfLeadingZeros() 這個方法是返回當前數值轉換為二進位制後,從高位到低位開始統計,看有多少個0連續在一塊。
//8 => 1000 numberOfLeadingZeros(8) = 28
//int是4個長度 100 32-3=29
//4 => 100 numberOfLeadingZeros(4) = 29
//ASHIFT = 31 - 29 = 2 ?? scale是區域性變數
//下標為5的資料
//ABASE + (5 << ASHIFT)
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
} catch (Exception e) {
throw new Error(e);
}
}
4_分解內部類 Node、TreeNode、ForwardingNode
Node
static class Node<K,V> implements Map.Entry<K,V> {
//這個hash的K的hash經過一次擾動運算得出來的
final int hash;
final K key; //執行緒安全不能修改
volatile V val; //val可能修改,保持執行緒可見性
volatile Node<K,V> next; //結構可能變化 保持可見性
Node(int hash, K key, V val, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.val = val;
this.next = next;
}
public final K getKey() { return key; }
public final V getValue() { return val; }
public final int hashCode() { return key.hashCode() ^ val.hashCode(); }
public final String toString(){ return key + "=" + val; }
public final V setValue(V value) {
throw new UnsupportedOperationException();
}
public final boolean equals(Object o) {
Object k, v, u; Map.Entry<?,?> e;
return ((o instanceof Map.Entry) &&
(k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
(v = e.getValue()) != null &&
(k == key || k.equals(key)) &&
(v == (u = val) || v.equals(u)));
}
/**
* Virtualized support for map.get(); overridden in subclasses.
* 連結串列情況下用不到,當前桶位變成 treebin fwd節點會用到
*/
Node<K,V> find(int h, Object k) {
Node<K,V> e = this;
if (k != null) {
do {
K ek;
if (e.hash == h &&
((ek = e.key) == k || (ek != null && k.equals(ek))))
return e;
} while ((e = e.next) != null);
}
return null;
}
}
TreeNode
/**
* Nodes for use in TreeBins
*/
static final class TreeNode<K,V> extends Node<K,V> {
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // needed to unlink next upon deletion
boolean red;
TreeNode(int hash, K key, V val, Node<K,V> next,
TreeNode<K,V> parent) {
super(hash, key, val, next);
this.parent = parent;
}
Node<K,V> find(int h, Object k) {
return findTreeNode(h, k, null);
}
/**
* Returns the TreeNode (or null if not found) for the given key
* starting at given root.
*/
final TreeNode<K,V> findTreeNode(int h, Object k, Class<?> kc) {
if (k != null) {
TreeNode<K,V> p = this;
do {
int ph, dir; K pk; TreeNode<K,V> q;
TreeNode<K,V> pl = p.left, pr = p.right;
if ((ph = p.hash) > h)
p = pl;
else if (ph < h)
p = pr;
else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
return p;
else if (pl == null)
p = pr;
else if (pr == null)
p = pl;
else if ((kc != null ||
(kc = comparableClassFor(k)) != null) &&
(dir = compareComparables(kc, k, pk)) != 0)
p = (dir < 0) ? pl : pr;
else if ((q = pr.findTreeNode(h, k, kc)) != null)
return q;
else
p = pl;
} while (p != null);
}
return null;
}
}
ForWaidingNode
/* ---------------- Special Nodes -------------- */
/**
* A node inserted at head of bins during transfer operations.
*/
static final class ForwardingNode<K,V> extends Node<K,V> {
//拿到fwd節點 代表現在再擴容資料正在遷移 寫執行緒:需要參與併發擴容 讀執行緒:呼叫find方法到新表繼續查詢
final Node<K,V>[] nextTable;
//hash固定
ForwardingNode(Node<K,V>[] tab) {
super(MOVED, null, null, null);
this.nextTable = tab;
}
Node<K,V> find(int h, Object k) {
// loop to avoid arbitrarily deep recursion on forwarding nodes
//tab 一定不為空
Node<K,V>[] tab = nextTable;
outer: for (;;) {
//n 表示為擴容而建立的 新表的長度
//e 表示在擴容而建立新表使用 定址演算法 得到的 桶位頭結點
Node<K,V> e; int n;
//條件一:永遠不成立
//條件二:永遠不成立
//條件三:永遠不成立
//條件四:在新擴容表中 重新定位 hash 對應的頭結點
//true -> 1.在oldTable中 對應的桶位在遷移之前就是null
// 2.擴容完成後,有其它寫執行緒,將此桶位設定為了null
if (k == null || tab == null || (n = tab.length) == 0 ||
(e = tabAt(tab, (n - 1) & h)) == null)
return null;
//前置條件:擴容後的表 對應hash的桶位一定不是null,e為此桶位的頭結點
//e可能為哪些node型別?
//1.node 型別
//2.TreeBin 型別
//3.FWD 型別
for (;;) {
//eh 新擴容後表指定桶位的當前節點的hash
//ek 新擴容後表指定桶位的當前節點的key
int eh; K ek;
//條件成立:說明新擴容 後的表,當前命中桶位中的資料,即為 查詢想要資料。
if ((eh = e.hash) == h &&
((ek = e.key) == k || (ek != null && k.equals(ek))))
return e;
//eh<0
//1.TreeBin 型別 2.FWD型別(新擴容的表,在併發很大的情況下,可能在此方法 再次拿到FWD型別..)
if (eh < 0) {
if (e instanceof ForwardingNode) {
tab = ((ForwardingNode<K,V>)e).nextTable;
continue outer;
}
else
//說明此桶位 為 TreeBin 節點,使用TreeBin.find 查詢紅黑樹中相應節點。
return e.find(h, k);
}
//前置條件:當前桶位頭結點 並沒有命中查詢,說明此桶位是 連結串列
//1.將當前元素 指向連結串列的下一個元素
//2.判斷當前元素的下一個位置 是否為空
// true->說明迭代到連結串列末尾,未找到對應的資料,返回Null
if ((e = e.next) == null)
return null;
}
}
}
}
5_小函式工具方法原始碼分解
5.1——spread(hash) 原始碼分析
/
* 1100 0011 1010 0101 0001 1100 0001 1110
* 0000 0000 0000 0000 1100 0011 1010 0101
* 1100 0011 1010 0101 1101 1111 1011 1011
* ---------------------------------------
* 1100 0011 1010 0101 1101 1111 1011 1011
* 0111 1111 1111 1111 1111 1111 1111 1111 ==HASH_BITS
* 0100 0011 1010 0101 1101 1111 1011 1011
*
* 當前的hash右移16位 亦或 原來的hash & HASH_BITS 得到正數的hash值
* 讓高16位 也參與到運算中來
*/
static final int spread(int h) {
return (h ^ (h >>> 16)) & HASH_BITS;
}
5.2——tabAt(tab, index) 原始碼分析
//獲取陣列指定下標位置的元素
@SuppressWarnings("unchecked")
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}
5.3——casTabAt(tab, index, c, v )原始碼分析
//用cas的方式去table的指定位置設定值,設定成功返回true
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
Node<K,V> c, Node<K,V> v) {
return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
5.4——setTabAt(tab, index, v) 原始碼分析
//指定位置設定值
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
}
5.5——resizeStamp(int n) 原始碼分析
/**
* Returns the stamp bits for resizing a table of size n.
* Must be negative when shifted left by RESIZE_STAMP_SHIFT.
* 擴容標識戳 一致才能參與擴容
* 16 -> 32
* numberOfLeadingZeros(16) => 1 0000 =>27 =>0000 0000 0001 1011
* |
* (1 << (RESIZE_STAMP_BITS - 1)) => 1000 0000 0000 0000 => 32768
* ---------------------------------------------------------------
* 0000 0000 0001 1011
* 1000 0000 0000 0000
* 1000 0000 0001 1011
*/
static final int resizeStamp(int n) {
return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
}
5.6——tableSizeFor(int c) 原始碼分析
/**
* Returns a power of two table size for the given desired capacity.
* See Hackers Delight, sec 3.2
* 返回>=c的最小的2的次方數
* c=28
* n=27 => 0b 11011
* 11011 | 01101 => 11111
* 11111 | 00111 => 11111
* ....
* => 11111 + 1 =100000 = 32
*/
private static final int tableSizeFor(int c) {
int n = c - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
相關文章
- 類內的靜態成員函式函式
- C++學習筆記(三):類與物件--靜態成員變數與常成員函式C++筆記物件變數函式
- 函式內部的變數提升函式變數
- Java中靜態程式碼塊、構造程式碼塊、建構函式、普通程式碼塊Java函式
- static變數,static程式碼塊,建構函式,程式碼塊等的載入順序變數C程式函式
- C++ 類成員函式C++函式
- Java內部類詳解-- 成員內部類Java
- 函式外與函式內的變數函式變數
- C++(常量成員函式)C++函式
- C++:類的成員函式C++函式
- Java中建構函式、靜態程式碼塊、程式碼塊的執行順序Java函式
- 關於變數的宣告和定義、內部函式和外部函式變數函式
- 如何在函式內部定義函式?函式
- 對於靜態成員來說是類的建構函式,對於例項成員是類的原型物件。函式原型物件
- 函式引數詳解函式
- 【GO學習二】包,函式,常量和變數Go函式變數
- C++靜態函式C++函式
- Java中的靜態內部類詳解Java
- 人工智慧---神經網路啟用函式恆等函式、sigmoid函式、softmax函式詳解人工智慧神經網路函式Sigmoid
- 引入const成員函式函式
- webgl內建函式--指數函式Web函式
- OC常用數學函式及常量函式
- OpenCV(cv::Mat 類的成員函式 ptr<T>())OpenCV函式
- [C++] 成員函式指標和函式指標C++函式指標
- 深入C++成員函式及虛擬函式表C++函式
- c++ const 成員函式C++函式
- 內部類與靜態內部類
- C++類的靜態成員變數初始化C++變數
- 類别範本中成員函式建立時機函式
- 類的靜態成員變數和普通成員變數該怎樣去區別定義變數
- 詳解Java函式式介面Java函式
- SetupDiGetClassDevs函式詳解dev函式
- 瞭解 JavaScript 函數語言程式設計 - 宣告式函式JavaScript函數程式設計函式
- ORALCE函式:LAG()和LEAD() 分析函式詳解函式
- C++建構函式和解構函式呼叫虛擬函式時使用靜態聯編C++函式
- C++ 派生類函式過載與虛擬函式繼承詳解C++函式繼承
- 如何使用成員函式指標函式指標
- 函式的動態引數 及函式巢狀函式巢狀