短视频直播源码,关于重入锁需要了解的内容
短视频直播源码,关于重入锁需要了解的内容
重入是指任意线程在获取到锁之后能再次获取该锁而不会被锁阻塞,基于这个定义会引出两个问题:
1、线程再次获取锁。锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是则再次获取成功。这里会涉及到锁获取的公平与非公平,下面会提到。
2、锁的最终释放。线程重复N次获取锁,随后在第N次释放锁后其它线程能够获取到锁。
我们知道synchronized是支持隐式的重入的,在synchronized修饰的方法如果出现递归,线程并不会阻塞自己。ReentrantLock是通过组合自定义同步器来实现锁的获取与释放,以非公平性(默认)实现。锁重入的次数是保存在volatile修饰的state变量上的,需要说明是重入锁并非共享锁,所以可以直接使用state进行记录,而后面提到的读写锁里的读锁因为是共享的,所以读锁的获取次数是使用threadlocal进行记录的。
ReentrantLock里有3个重要的内部类,分别是 Sync、NonfairSync、FairSync:
1、Sync 是后面两个的父类,继承至AbstractQueuedSynchronizer。
2、NonfairSync和FairSync都继承至Sync。
3、NonfairSync 主要用于实现非公平锁,FairSync 主要用于实现公平锁。
private final Sync sync;
在构造方法中初始化,通过构造方法参数决定使用公平锁还是非公平锁实现。
//不公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
//公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
公平与是针对获取锁而言的,如果一个锁是公平的,那么锁的获取顺序就应该符合请求的绝对时间顺序,也就是FIFO。
下面我们来比较下公平锁与非公平锁获取的代码:
static final class NonfairSync extends Sync {
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
* 非公平锁上来先CAS设置状态,如果成功则获取成功,这是与公平锁不同点之一
* 未成功才会加入到同步队列中
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
//获取非公平锁
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;
}
//公平获取锁
static final class FairSync extends Sync {
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
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