上一篇文章已完整展示了等待队列的管理(添加结点、移除取消结点)、独占模式下的acquire操作、acquire中断取消、前驱如何通知后继。这些知识已足够用来实现一个可重入锁。
本篇通过java.util.concurrent.locks.ReentrantLock
类的源码来分析如何实现可重入锁。
可重入锁
可重入锁就是说当线程拥有这把锁的时候,它再次请求锁是成功的;当线程释放锁时,如果持有锁的线程对锁的请求次数大于释放次数,则该线程仍然拥有锁,直到请求次数与释放次数相等时才真正释放锁。
所以可重入锁需要一个重入计数变量,初始值设为0,当成功请求锁时加1,释放锁时减1,当释放锁之后计数为0则真正释放锁。重入锁还必须持有对锁持有者的引用,用以判断是否可以重入。
锁的公平性
如果锁能够严格按照线程请求锁的先后顺序分配锁,则认为锁具有公平性;如果某一线程能在其他等待线程之前获取到锁,则认为锁不具有公平性。
ReentrantLock
ReentrantLock
是JUC包里可重入的独占锁实现,它具有三个内部类:Sync、NonfairSync、FairSync
,通过构造函数的参数来指定锁是否是公平的,下面是一些核心代码:
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
public void lock() {
sync.lock();
}
public void unlock() {
sync.release(1); // 这个1表示退出锁1次。
}
// 带超时限制的获取
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
// 其他代码省略
}
可以看到,ReentrantLock
都是把具体实现委托给内部类而不是直接继承自AbstractQueuedSynchronizer
,这样的好处是用户不会看到不需要的方法,也避免了用户错误地使用AbstractQueuedSynchronizer
的公开方法而导致错误。
ReentrantLock
的重入计数是使用AbstractQueuedSynchronizer
的state
属性的,state
大于0表示锁被占用、等于0表示空闲,小于0则是重入次数太多导致溢出了。
继续阅读 →