ReentrantLock加锁及解锁过程之源码分析
Posted 八阿哥克星
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ReentrantLock加锁及解锁过程之源码分析相关的知识,希望对你有一定的参考价值。
一、ReentrantLock锁lock() 流程
首先不论公平锁还是非公平锁,都会调用下面的(非公平锁显示用cas尝试获取锁,获取不到才进入下面的acquire())
公平锁和非公平锁(在没有指定的情况下,默认是非公平)有不同的tryAcquire方法实现,公平锁是在tryAcquire方法中先对锁状态做判断,是否为0,为0则表示当前没有线程持有锁,可获取,这里,公平锁会进行hasQueuedPredecessors,判断在阻塞队列里是否有线程,并且判断是不是当前线程,若有且并不是当前线程直接返回false;如果锁状态不为0的情况下,判断当前线程是不是exclusiveOwnerThread,是则代表当前要获取锁的线程和正在持有锁的线程为同一线程,基于可重入的原则,获取锁成功,锁状态加一,若不是,则直接返回false;
//公平锁的tryAcquire
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) { //用cas方式获取锁
setExclusiveOwnerThread(current); //获取锁成功,将exclusiveOwnerThread
//设为当前线程
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;
}
public final boolean hasQueuedPredecessors() {
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());
}
若是非公平锁,则没有此步骤,判断当前锁状态是否为0,是0直接cas尝试获取锁,其他过程与公平锁并无二致;
如果经过上述过程没有获取到锁,就会将当前线程放到阻塞队列中并阻塞该线程,即执行acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法;
(addWaiter是创建node节点对象,并将其放到阻塞队列(双向列表)的尾部,acquireQueued是有一个死循环,在此循环中
经过判断shouldParkAfterFailedAcquire(p, node)是否需要阻塞,是,则阻塞,否,继续进行循环,阻塞后的线程等待唤醒,唤醒后继续执行循环,
尝试获取锁,如此往复:
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)) { //判断当前节点是否为头节点的next节点,是
//尝试获取锁;
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
二、ReentrantLock锁unlock() 流程
释放锁时,首先调用tryRelease,对锁状态减一,为0时,则返回true,并将exclusiveOwnerThread置为null,表示当前所可以获取,没被线程持有
protected final boolean tryRelease(int releases) { //releases值为1
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;
}
tryRelease返回true后 ,调用unparkSuccessor,唤醒头部节点的next节点对应线程,该线程继续执行上述acquireQueued中代码;
三、condition的await()与signal()方法
condition.await()方法,是指对持有锁的线程进去等待状态,加入到等待队列(单向链表),并且释放锁
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter(); //将当前线程封装成Node节点,放到等待队列中
long savedState = fullyRelease(node); //释放锁
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this); //将当前线程置为阻塞状态
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
fullyRelease方法中调用了上面的releas方法,释放锁,并且唤醒阻塞队列的头节点的next节点对应线程;
final long fullyRelease(Node node) {
boolean failed = true;
try {
long savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
condition.signal()方法,调用了doSignal方法中的transferForSignal,将等待队列的最早进入的线程对应的Node加入到阻塞队列,并判断Node中的waitStatus状态,满足(waitStatus> 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL),会用LockSupport.unpark(node.thread)将此线程唤醒。
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
以上是关于ReentrantLock加锁及解锁过程之源码分析的主要内容,如果未能解决你的问题,请参考以下文章