与读锁加锁的过程类似,写锁的加锁过程分为如下几步:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
首先我们先进入tryAcquire方法中(本文仍是以非公平锁为例)
protected final boolean tryAcquire(int acquires) {
/*
* Walkthrough:
* 1. If read count nonzero or write count nonzero
* and owner is a different thread, fail.
* 2. If count would saturate, fail. (This can only
* happen if count is already nonzero.)
* 3. Otherwise, this thread is eligible for lock if
* it is either a reentrant acquire or
* queue policy allows it. If so, update state
* and set owner.
*/
Thread current = Thread.currentThread();
int c = getState();
//获取持有写锁数量
int w = exclusiveCount(c);
//因为读写互斥、写写互斥,所以c!=0表示有别的线程持有锁
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
//如果持有锁的线程不是当前线程,即表明不是写锁重入的情况下,直接return false
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
//写锁重入,无需CAS,无法做到CAS并发
setState(c + acquires);
return true;
}
//writerShouldBlock()永远返回false,因为偏向锁概念即写锁优先
//CAS加锁失败false
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
//成功将该所置为独占锁的持有者
setExclusiveOwnerThread(current);
return true;
}
如果枷锁失败的话需要尝试将该线程加入到QAS队列中,其方法为addWaiter,这部分过程与ReentrantLock一样。这里不做多介绍。
写锁释放
对于释放过程,其过程如下所示:
public final boolean release(int arg) {
//tryRelease已经全部释放掉了
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);//唤醒后续线程
return true;
}
return false;
}
首先我们进入tryRelease过程
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//低16为表示写锁的持有数量
int nextc = getState() - releases;
//该线程所重入的线程已经全部释放了
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}