引言:
在上一篇博文我们介绍了ConcurrentHashMap在jdk1.8中所必要的知识,作为基础入门。因为jdk1.8的ConcurrentHashMap做了太多的变动,所以新知识学习是必要的。今天是ConcurrentHashMap的第二篇,第二篇主要是认识ConcurrentHashMap,我将会介绍一下它的关键成员变量和一些关键的类。大家可以结合前几篇博文的HashMap的知识,很多还是很相似的。笔者目前整理的一些blog针对面试都是超高频出现的。大家可以点击链接:http://blog.youkuaiyun.com/u012403290
如何实现线程安全:
我们都知道ConcurrentHashMap核心是线程安全的,那么它又是用什么来实现线程安全的呢?在jdk1.8中主要是采用了CAS算法实现线程安全的。在上一篇博文中已经介绍了CAS的无锁操作,这里不再赘述。同时它通过CAS算法又实现了3种原子操作(线程安全的保障就是操作具有原子性),下面我赋值了源码分别表示哪些成员变量采用了CAS算法,然后又是哪些方法实现了操作的原子性:
// Unsafe mechanics CAS保障了哪些成员变量操作是原子性的
private static final sun.misc.Unsafe U;
private static final long LOCKSTATE;
static {
try {
U = sun.misc.Unsafe.getUnsafe();
Class<?> k = TreeBin.class; //操作TreeBin,后面会介绍这个类
LOCKSTATE = U.objectFieldOffset
(k.getDeclaredField("lockState"));
} catch (Exception e) {
throw new Error(e);
}
}
--------------------------------------------------------------------------------------
private static final sun.misc.Unsafe U;
private static final long SIZECTL;
private static final long TRANSFERINDEX;
private static final long BASECOUNT;
private static final long CELLSBUSY;
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);
int scale = U.arrayIndexScale(ak);
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
} catch (Exception e) {
throw new Error(e);
}
}
//3个原子性操作方法:
/* ---------------- Table element access -------------- */
/*
* Volatile 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 in principle require only release ordering, not
* full volatile semantics, but are currently coded as volatile
* writes to be conservative.
*/
@SuppressWarnings("unchecked")
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
return (Node<K,V>)U.getObjectVolatile(tab, ((long<