先说结果,CAS与volatile都拥有内存可见性和内存屏障,也就是说cas具备与volatile一样的volatile读与volatile写。
volatile
ReenteantLock 使用volatile来修饰了state变量,volatile具有这些特点:保证内存可见性,以及防止指令重排序。
如何保证内存可见性
底层通过使用lock
指令来实现volatile。其中lock
指令在多核处理器下会有以下操作:
- 将当前处理器中缓存行中的数据立即写回主存。
- 数据写回主存后,会导致其他线程所持有的旧数据的内存地址数据无效。
从而保证了内存可见性。
如何防止指令重排序
volatile底层使用内存屏障来防止指令重排序,具体的内存屏障有以下几种:
LoadLoad屏障:
抽象场景:Load1; LoadLoad; Load2
Load1 和 Load2 代表两条读取指令。在Load2要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
StoreStore屏障:
抽象场景:Store1; StoreStore; Store2
Store1 和 Store2代表两条写入指令。在Store2写入执行前,保证Store1的写入操作对其它处理器可见
LoadStore屏障:
抽象场景:Load1; LoadStore; Store2
在Store2被写入前,保证Load1要读取的数据被读取完毕。
StoreLoad屏障:
抽象场景:Store1; StoreLoad; Load2
在Load2读取操作执行前,保证Store1的写入对所有处理器可见。StoreLoad屏障的开销是四种屏障中最大的。
Unsafe包中的CAS(compareAndSwap)
Unsafe包中的方法都为native方法,所以。cas也是通过第三方语言(C++)实现。实现cas时,也用到了lock
指令。这也就是为什么cas也具有volatile读和volatile写的内存语义了。
在ReentrantLock
中,分为fairLock
公平锁与nonfairLock
。
释放锁
其中释放公平锁和非公平锁时,都会cas
去写state
变量。
为什么要去写state
呢,state
变量在ReentrantLock
是一个重要的存在,通过state
可以知道锁是否是被持有。如果state > 0
,表示当前锁有线程持有。如果state == 0
说明当前锁没有被任何线程持有。
所以,当线程使用完锁以后,必须把state
的状态改写为0
。这样才代表当前锁可用。
获取锁
公平锁的源码:
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
公平锁相对于非公平锁多了一个!hasQueuedPredecessors()
方法的调用。
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
该方法主要是判断当前线程是否处于队列中第一个。如果是则返回true,否则返回false。// TODO