目录
5.2 hasQueuedPredecessors() 方法原理
6.2 tryLock(long timeout, TimeUnit unit)
ReentrantLock 是可重入的独享锁。根据参数可决定其内部是一个公平锁还是非公平锁。
1. ReentrantLock 初始化
// 非公平锁
ReentrantLock lock = new ReentrantLock();
// 公平锁
ReentrantLock lock = new ReentrantLock(true);
ReentrantLock 默认为非公平锁,可传入参数 true 来获取公平锁。其子类 FairSync 和 NonfairSync 分别实现了锁的公平和非公平策略。ReentrantLock 初始化源码如下:
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
2. ReentrantLock 内部结构
- ReentrantLock 实现了 Lock 接口
- 内部子类 Sync 继承 AbstractQueuedSynchronizer 抽象类
- 内部子类 FairSync 和 NonfairSync 继承 Sync 类
3. ReentrantLock 常用方法
- lock () 上锁方法
- 执行逻辑:lock () -> acquire () -> tryAcquire ()
- unlock () 解锁方法
- tryLock(),tryLock(long timeout, TimeUnit unit) 尝试获取锁
4. 非公平锁 NonfairSync
4.1 lock() 方法原理
i. 基于 CAS 将 state 从 0 设置为 1
ii. 如果设置成功,将独享锁的线程设置为当前线程
iii. 请求失败后,进行一次重试
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
// 基于 CAS 尝试将 state 从 0 设置为 1
if (compareAndSetState(0, 1))
// 独享锁的线程设置为当前线程
setExclusiveOwnerThread(Thread.currentThread());
else
// 重试一次(内部调用 tryAcquire 方法)
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
如下获取锁失败后执行的 AQS 中的 acquire 方法。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
由于 AQS 并没有提供可用的 tryAcquire 方法,tryAcquire 方法需要子类自己定制化,所以此处 tryAcquire 会调用 NonfairSync 中实现的 tryAcquire 方法。
4.2. TryAcquire() 方法原理
NonfairSync 的 tryAcquire 方法内调用了 nonfairTryAcquire 方法
nonfairTryAcquire 源码如下:
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 判断 state 是否为 0
if (c == 0) {
// 尝试 state 的值从 0 改为 1
if (compareAndSetState(0, acquires)) {
// 设置获取锁的线程为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) { // 如果持有锁的线程为当前线程
// state 的值 + acquire
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 设置 state 值为新值
setState(nextc);
return true;
}
return false;
}
i. 判断 state 是否为 0 ,是 0 则尝试获取锁
ii. 不为 0 ,判断持有锁的线程是否是当前线程
iii. 如果为当前线程,则将 state + acquire 重新对 state 赋值 (所以 ReentrantLock 为可重入锁)
iv. 持有锁的线程非当前线程,则返回 false
5. 公平锁 FairSync
5.1 lock()、TryAcquire() 方法原理
公平锁与非公平锁的原理类似,区别在于 tryAcquire 方法多了 hasQueuedPredecessors 的判断:
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 公平锁比非公平锁多了 hasQueuedPredecessors 的判断
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;
}
}
5.2 hasQueuedPredecessors() 方法原理
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
(
(s = h.next) == null ||
s.thread != Thread.currentThread()
);
}
i. 如果头结点与尾结点不相等( h! = t ),说明不止一个线程请求锁
ii. 判断 head.next 是否为 null,如果为 null,返回 true
h != t 的同时 h.next==null 的情况:有其他线程第一次正在入队时,compareAndSetHead (node) 完成,还没执行 tail=head 语句时,此时 tail=null,head=newNode,head.next=null
iii. s.thread != Thread.currentThread() 判断当前线程是否为第二个线程
hasQueuedPredecessors 的判断保证了线程获取锁的顺序行,所以 FairSync 实现了公平锁。
6. tryLock 方法
6.1 tryLock()
尝试获取锁,获取到锁则返回 true,获取失败则返回 false
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
tryLock 内部调用的 nonfairTryAcquire 与非公平锁代码一致,所以 tryLock() 使用的是非公平策略。
6.2 tryLock(long timeout, TimeUnit unit)
与 tryLock() 的区别:此方法可以设置超时时间,超时时间到没有获取到该锁则返回 false。
7. ReentrantLock 的使用
lock.lock();
try {
//TODO
} finally {
lock.unlock();
}
8. 总结
i. ReentrantLock 的底层是使用 AQS 实现的可重入独享锁
ii. ReentrantLock 内部有公平与非公平两种实现,默认是非公平
iii. 对于 AQS 中 state,加锁 state + 1,解锁 state - 1,所以加锁几次就要解锁几次
iv. state 为 0 表示当前锁空闲,大于或等于 1 ,说明该锁已经被占用