ReentrantLock
给代码加锁可以使用synchronized进行加锁,这种加锁和释放锁是隐式的。也可以使用ReentrantLock进行显示的加锁和释放锁,分别对应lock和unlock方法。
ReentrantLock是一个互斥锁,分为公平锁和非公平锁。当我们创建一个ReentrantLock时默认是非公平锁,因为非公平锁的效率比公平锁的效率高,但也带来一个问题是非公平锁中的线程可能会有饿死的情况。
非公平锁
ReentrantLock中维护了一个内部类Sync,该内部类继承了AQS,实现了AQS中的tryAcquire、tryRelease等方法;而非公平锁类是NonfairSync,该类继承了Sync,实现了lock和tryAcquire方法。
lock
非公平锁NonfairSync中的lock方法如下
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
- 首先使用CAS的方式设置state值为1,如果设置成功,则调用setExclusiveOwnerThread方法,将当前线程设置成独占锁
- 如果设置失败,调用acquire方法,这里调用的是AQS中的acquire方法,这个方法在之前的AQS中介绍了,在acquire方法中会首先调用子类的tryAcquire方法,这里就会调用NonfairSync类的tryAcquire方法
tryAcquire
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
这个方法很简单,直接调用nonfairTryAcquire方法将需要设置的state值传入。这个方法的具体实现是在Sync类中
nonfairTryAcquire
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;
}
- 获取当前线程,获取state值c
- 如果state为0,说明是可以获取锁的,那么使用CAS的方式将state的值从0改为1,如果成功,那么将当前线程设置为独占锁,并返回true
- 如果state不为0或者CAS设置state失败,那么就说明有线程获取了锁,从getExclusiveOwnerThread中获取线程和当前相比
- 如果不相等,说明不是同一个锁,返回false,接着进入AQS,将当前线程加入到同步队列中
- 如果相等,说明是重入锁,将c和acquire相加得到nextc,如果nextc小于0,说明不合法,直接抛出异常;如果大于0,则将最新的值写入state并返回true,获取锁成功
公平锁
公平锁的类是FairSync,继承了Sync类,该类中同样只有lock和tryAcquire方法。
lock
公平锁的lock和非公平锁的lock方法相比不同之处在于:公平锁lock方法直接调用的acquire方法,并没有CAS设置state这一步
final void lock() {
acquire(1);
}
tryAcquire
当使用公平锁获取锁的时候会调用tryAcquire方法,该方法和非公平锁大体逻辑一致,不同之处在于:如果state为0,说明当前线程是可以获取锁的,但在获取锁之前需要进行一个判断:是否有任何线程等待获取锁的时间超过当前线程的判断
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
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());
}
这里主要说hasQueuedPredecessors方法:
- 如果h 等于 t 说明队列中没有节点,返回false,那么!hasQueuedPredecessors()就为true,说明该线程是可以获取锁的
- 如果h不等于t(说明同步队列中有节点) && (如果头结点的后继s不为null并且s的线程和当前线程相等,则返回false,表示表示当前线程就是等待时间最长的线程),返回false
以上就是公平锁和非公平锁获取锁的过程
释放锁
公平锁和非公平锁释放锁的逻辑都是在Sync中,首先通过ReentrantLock的unlock方法释放锁,调用的是AQS的release方法,在AQS的release方法中会调用子类的tryRelease方法,这时候就会调用Sync类的tryRelease方法
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;
}
- 传入需要释放的state值,获取AQS中的state值减去传入的值得到c
- 判断当前线程是否是独占锁的线程,如果不是抛出异常
- 如果相等,判断c是否为0,如果为0,将独占锁的线程置为null
- 修改state值,返回free,如果为true,说明可以下次别的线程可以获取锁,如果为false,则不能,因为该线程还占有着锁,这是一个重入锁
当然该类也支持中断式获取锁以及超时获取锁,原理大致相同,底层都是依赖AQS。
ReentrantLock的等待唤醒机制使用的是Condition,底层调用的是AQS的内部类ConditionObject中的方法。这些都在AQS中说过
以上就是ReentrantLock的整体逻辑

浙公网安备 33010602011771号