加锁
final void lock() {
//CAS加锁,成功将该线程置为独占锁持有者--setExclusiveOwnerThread
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
如果CAS加锁失败的话,则进入acquire(1),如果成功则将当前线程设为独占锁的拥有者。接下来我们看枷锁失败后的处理。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
如果加锁失败,我们会先再次尝试加入锁,其实现在tryAcquire函数中,这里只介绍非公平锁实现方式,其源码如下所示:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
在ReentrantLock中,c表示其加锁数量,在读写锁中,高16位表示读锁数量,低16位表示写锁数量。ReentrantLock中,c==0表示当前无锁占用,则会再次尝试CAS加锁,如果加锁失败,查看该线程是否为占有锁的线程,这也是可重入锁的概念,如果是重入锁的话则锁的数量加一,否则尝试加锁失败。
如果加锁失败,我们需要将该线程放入到阻塞队列中。
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
注意的是,将节点放入到队列中也是基于CAS的形式放入到CAS中的。
放入到阻塞队列后,并不会马上中断,这有一个自旋的过程,超过一定时间则会中断。其过程如下所示:
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
//如果该节点的前驱是头节点,头节点表示正在运行的线程节点
//则会再次尝试加锁,成功则将该节点置为头节点
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//失败的则会先判定是否应该暂停,其过程看下述代码
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
判定是否应该阻塞的代码
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
当前线程的后续线程需要被唤醒,即node现在需要阻塞
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
//前驱结点取消了则前驱结点向前移动
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
}
//其他情况将pred状态置为SIGNAL
else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
parkAndCheckInterrupt函数则将其阻塞,等待被唤醒。
唤醒过程
唤醒过程就是尝试释放锁的过程,释放成功才会进入唤醒状态,waitStatus!=0没看懂,他的四种状态没有0的枚举,暂且认为h.waitStatus != 0永远为true,即队列不空就唤醒线程。
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
尝试释放所得过程比较简单,因为是可重入锁,所以得确保当前线程的c==0,才释放掉独占锁的线程
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
//释放该线程
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}