ReentrantLock也叫做可重入锁,就是拿到锁的线程可以多次获得锁,体现在代码层面就是可以多次调用lock成功。
一、基本结构
下面是重入锁基本代码结构了,Sync继承了AQS(可以点击查看之前的博客解读),然后加锁和解锁是迪调用Sync的方法。所以只要理解了AQS,那么掌握重入锁就是水到渠成的事情了。
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
abstract void lock();
}
static final class FairSync extends Sync {}
static final class NonfairSync extends Sync {}
public void lock() {
sync.lock();
}
public void unlock() {
sync.release(1);
}
二、加锁
1.公平锁
static final class FairSync extends Sync {
// 公平锁的加锁开始不会参与锁竞争,会加入到先进先出的队列排队获取锁
final void lock() {
acquire(1);
}
}
// 这是AQS类里面的方法,在AQS的文章讲过
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// 这里只讲解tryAcquire方法,acquireQueued和addWaiter方法在AQS文章讲过
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 状态为0说明没有线程获得锁,则可以参与锁竞争
if (c == 0) {
// 参与锁竞争,首先判断队列中是否有线程在排队,没有则直接锁竞争,调用CAS。
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
// 获得锁,则设置当前线程占有锁
setExclusiveOwnerThread(current);
return true;
}
}
// 这里就是锁能够重入的证明,如果当前线程获得了锁,则直接更改锁状态,并且返回true代表获得锁
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
// 如果最后没有获得锁,则返回false,进入队列排队
return false;
}
2.非公平锁
非公平锁与公平锁在代码中的区别是多了个if else,它的非公平在于,每次调用lock时线程首先参与一次锁竞争,如果竞争失败再走公平锁的流程。
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
三、解锁
公平锁和非公平锁的解锁方法都是同一个
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
// 如果队列还有其他线程等待获取锁则唤醒后继的等待线程
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
// 锁状态计数器减数
int c = getState() - releases;
// 解锁必须是占有锁的线程,否则抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 状态为0,说明没有任何线程获取锁了
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
四、总结
- 获得锁的线程可以重复获取锁,但是锁计数器是int值,需要注意不要溢出了。
- 重入锁的挂起是使用了AQS的方法,里面是用了LockSupport的park方法。
- 重入锁有公平和非公平两种,默认是使用非公平锁。