ReentrantLock源码解析
简介
ReentrantLock是一种基于AQS
框架的应用实现,是JDK中的一种线程并发访问的同步手段,它的功能类似于synchronized,是一种互斥锁,可以保证线程安全。
相对于 synchronized, ReentrantLock具备如下特点:
- 可中断
- 可以设置超时时间
- 可以设置为公平锁(默认非公平)
- 支持多个条件变量
- 与 synchronized 一样,都支持可重入
ReentrantLock最重要的是三个内部类Sync、NonfairSync和FairSync。
1、Sync
1.1、 nonfairTryAcquire(int acquires)
非公平锁尝试获取锁。逻辑简单,在AQS中已经讲过了。
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;
// int最高位为正负标记位
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
1.2、释放锁 tryRelease(int releases)
只有释放锁完成后满足 c == 0
才返回true,因为重入锁获取了几次就要释放几次。
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;
}
1.3、abstract void lock()
抽象方法,用于子类继承,在公平锁和非公平锁种会分别做具体的实现。
2、NonfairSync extends Sync
非公平锁主要重写两个方法 lock()
和 tryAcquire(int acquires)
2.1 lock()
因为是非公平锁,所以获取锁的时候不排队,先尝试获取锁,获取失败则调用AQS的 acquire() 方法。
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
2.2 tryAcquire(int acquires)
在AQS中说过,子类必须自己写具体的获取锁的逻辑即重写 tryAcquire(int acquires)
方法。
而在非公平锁种, tryAcquire(int acquires)
其实是调用的父类 Sync
的 nonfairTryAcquire(acquires)
,同样是先直接尝试获取锁。
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
2.3 NonfairSync 小结
ReentrantLock的非公平锁在调用Lock() 方法时,会先进行两次CAS尝试获取锁,获取失败后才进入同步队列排队。
3、FairSync extends Sync
3.1 lock()
公平锁的lock()直接调用AQS的 acquire(1)
方法,而不是像非公平锁一样先尝试获取锁。
final void lock() {
acquire(1);
}
3.2 tryAcquire(int acquires)
在公平锁中,tryAcquire(int acquires)
方法和非公平锁的区别是,不再可以直接CAS尝试获取锁,而是通过AQS 的排队校验方法 hasQueuedPredecessors()
来判断是否需要排队,如果不需要才能尝试获取锁,体现其公平性。
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;
}
3.3 FairSync 小结
ReentrantLock的公平锁在调用Lock() 方法时,判断当前线程是否需要排队来决定是否能够尝试获取锁,相比于非公平锁的两次直接CAS,这样做保证了其公平性(其实公平锁也不是完全公平)。