ReentrantLock 示例
private ReentrantLock lock = new ReentrantLock(true);
public void f(){
try {
lock.lock();
//do something
}
finally {
lock.unlock();
}
}
源码解析(公平锁-unlock流程)
ReentrantLock#unlock()
public void unlock() {
sync.release(1);
}
AbstractQueuedSynchronizer#release(int arg)
public final boolean release(int arg) {
//尝试释放锁,只有当 state == 0 才会释放成功
if (tryRelease(arg)) {
//释放成功之后,查看head节点,如果head节点不为 null,并且,head节点不为0 (可能为 -1)
Node h = head;
if (h != null && h.waitStatus != 0)
//unpark 线程
unparkSuccessor(h);
return true;
}
return false;
}
ReentrantLock.Sync#tryRelease(int arg)
protected final boolean tryRelease(int releases) {
//state - releases,上锁之后,state > 0,所以释放锁要减掉
int c = getState() - releases;
//如果当前线程并不是持有锁的线程,不能乱释放锁,直接给你抛个异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
//释放标志
boolean free = false;
//由于是可重入锁,所以当 c == 0 的情况下才真正释放完成
if (c == 0) {
free = true;
//设置当前线程为空
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
AbstractQueuedSynchronizer#unparkSuccessor(Node node)
private void unparkSuccessor(Node node) {
//获取head node节点的 waitStatus , < 0(-1) 就CAS 改回 0
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
//找到下一个节点
Node s = node.next;
//如果下一个节点为null,或者 已经取消抢锁了,继续往下找
if (s == null || s.waitStatus > 0) {
s = null;
//从尾部向前遍历,如果 waitStatus<=0 就赋值给 s,这样保证最前边的一个waitStatus<=0的线程可以唤醒。
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
//如果下一个节点不为 null,直接 unpark 线程,对应代码就是AbstractQueuedSynchronizer#acquireQueued(Node node, int arg) 中 的for(;;),这样线程唤醒之后,就会尝试再去获取锁了。
if (s != null)
LockSupport.unpark(s.thread);
}
总结
释放锁的流程比较简单,就是讲 state
减去1,然后去队列里面找下一个节点,最后执行 unpark
方法唤醒线程。