背面试题中的cas锁,看得人一头雾水,什么比较和替换,云里雾里在实际业使用务中咱们也很难使用到没有场场景现在就从源码出发开始从ConcurrentHashMap开始看cas,其中ConcurrentHashMap的初始化方法是典中典的双检查cas锁。
//初始化方法
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
//此处再去判断是否是空,为了线程安全(假如都有多个线程都进入到这里的话就会又去判断一遍)
while ((tab = table) == null || tab.length == 0) {
//假如sizeCtl是下次要扩容的大小
//假如他得-1<0的时候证明有线程正在在里面进行扩容
if ((sc = sizeCtl) < 0)
//让这个线程放弃cpu的使用权进入返回就绪态
//就是说让他放弃争夺权,仅仅就是空转
Thread.yield(); // lost initialization race; just spin
//SIZECTL = U.objectFieldOffset(k.getDeclaredField("sizeCtl"));
//获取成员变量sizeCtl相对于当前对象的内存中的偏移值
//U的compareAndSwapInt(对象,偏移量,期望值,赋给成员变量的值)
//其实就是将内存中的对象的成员变量查出来与期望值相对,如果相等就讲-1赋给内存中的成员变量
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
//在此处还是要去判断一下是否完成初始化完成保证线程安全
if ((tab = table) == null || tab.length == 0) {
//此处就是sizeCtl是否大于0,如果大于0就说明new的时候传参了,大小就会变成传参的值
//没大于0就是没传参,大小就是默认值16
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
//先创建node数组
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
//并将sc的值变成扩容0.75sc(大小变成啥时需要去扩容)
sc = n - (n >>> 2);
}
} finally {
//传递回成员变量
sizeCtl = sc;
}
break;
}
}
//返回数组
return tab;
}