在學習ConcurrentHashMap
時發現,源碼中對table
數組的元素進行操作時,使用了三個封裝好的原子操作方法,如下:
/* ---------------- Table element access -------------- */
/*
* Atomic access methods are used for table elements as well as
* elements of in-progress next table while resizing. All uses of
* the tab arguments must be null checked by callers. All callers
* also paranoically precheck that tab's length is not zero (or an
* equivalent check), thus ensuring that any index argument taking
* the form of a hash value anded with (length - 1) is a valid
* index. Note that, to be correct wrt arbitrary concurrency
* errors by users, these checks must operate on local variables,
* which accounts for some odd-looking inline assignments below.
* Note that calls to setTabAt always occur within locked regions,
* and so require only release ordering.
*/
@SuppressWarnings("unchecked")
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
return (Node<K,V>)U.getObjectAcquire(tab, ((long)i << ASHIFT) + ABASE);
}
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
Node<K,V> c, Node<K,V> v) {
return U.compareAndSetObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
U.putObjectRelease(tab, ((long)i << ASHIFT) + ABASE, v);
}
casTabAt
這個方法我們可以很清晰地明白它封裝了對於數組元素的cas操作,但是另外兩個方法的意義如何理解呢?
源碼的作者使用Unsafe
直接通過數組內存地址以及索引偏移量去訪問和修改數組元素的值,和我們直接使用java代碼訪問(arr[i]
)、賦值(arr[i] = x
)有什麼區別呢?
請教了成哥(同事)後得到了一個重要的點:數組越界異常ArrayIndexOutOfBoundsException
如果java中的數組和c一樣,僅僅是一個指針的話,那麼也許通過arr[i]
訪問和通過內存地址訪問不會有什麼區別,但是由於ArrayIndexOutOfBoundsException
這個衆所周知的異常,我們可以推斷:java中的數組是經過了包裝的
另一個可以從側面印證的點是arr.length
大概搜索了一下瞭解到以下的知識(不保證正確):
- jvm會爲數組對象動態創建Class類文件,其標識爲
[|
- (HotSpot VM中)java對象的對象頭(Object header)內會有一段用於記錄數組長度的數據
不敢再深挖了,感覺是個大坑。。
總結:ConcurrentHashMap
中針對table
數組的Unsafe
訪問和賦值是存在優化的意義的。
以上爲拋磚引玉。。