RTL(ReentrantLock) 可重入锁学习笔记
什么是ReentrantLock 及其作用
ReentrantLock 基于 AQS(AbstractQueuedSynchronized),在并发编程中它可以实现公平锁和非公平锁来对资源进行同步,同时和synchronized一样,ReentrantLock支持可重入。ReentrantLock在调度上更灵活,支持更多丰富的功能。
可重入性
在并发编程中可重入性指的是 单个线程重新进入同一个子程序仍然是线程安全的 。一个线程可以不用释放而重复获取某个锁n次,只是在释放时需要相应释放n次。
死锁:若A线程既获取了锁,又在等待自己释放锁就会造成死锁。
公平锁 & 非公平锁
- 非公平锁:不按照请求锁的顺序分配,不一定拥有获取锁的机会,但性能可能比公平锁高。
- 公平锁:按照请求锁的顺序分配,拥有锁的机会稳定,但性能可能比非公平锁低。
Lock接口定义
RTL 实现了Lock接口,而Lock定义了6个方法,这6个方法定义了一些广泛的操作边界。
- void Lock()方法:获取锁,加入当前锁被其他线程占用,则等待直到获取为止。
- void lockInteruptibly() throws InterruptedException;方法:获取锁,假如当前线程在等待锁的过程中被中断,那么会退出等待,并抛出中断异常。
- boolean tryLock(); 尝试获取锁,并立即返回,返回值代表的是是否成功获取到锁。
- boolean tryLock(long time, TimeUnit unit) throws InterruptedException; 在一段时间内尝试获取锁,假如期间被中断,则抛出中断异常。
- void unlock(); 释放锁。
- Condition newCondition(); 新建一个绑定在当前Lock上的Condition对象。
Conditon对象,表示一个等待状态, 获得锁的线程在某些时刻需要等待一些条件的完成才能继续执行,可以通过await()方法注册在Condition上进行等待,然后通过Condition方法的signal()方法将其唤醒。一个Lock对象可以关联多个Condition,多个线程可以被绑定到不同的Condition上,就可以分组唤醒。Condition还提供了和限时/中断相关的功能,丰富了线程的调度策略。
RTL三个核心内部类Sync & NonfairSync & FairSync
Sync
abstract static class Sync extends AbstractQueuedSynchronizer {...}
- RTL内部类Sync继承了AQS,说明AQS中所有预设的机制Sync都可以使用.
- abstract 修饰Sync内部类,说明Sync需要通过其子类来进行实例化。NonfairSync和FairSync是Sync唯二的两个子类。
NonfairSync
非公平锁的具体实现
- NonfairSync的具体实现中,Lock()方法内上来直接插队,先进行两次非公平的获取锁,若这两次获取锁失败,那么将进入FIFO队列进行排队直到获取锁。
- 而tryAquire方法则是进行一次插队尝试获取锁,如果确实不想排队,那么上层可以写个循环不断调用这个方法尝试插队获取锁。
/**
* Sync object for non-fair locks
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
FairSync
公平锁的具体实现
- lock()方法直接调用AQS的acquire()方法
- tryAcquire()实现AQS接口,若当前线程已经获取锁,则对state值进行累加操作,可以继续使用锁,实现可重入。若锁一开始是空闲的,那么允许直接拿锁,若不空闲判断累加或者直接入队排号。
/**
* Sync object for fair locks
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
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);
return true;
}
return false;
}
}
RTL 的构造函数
ReentrantLock有两个构造函数
- 默认无参构造函数使用了非公平锁的模式
- 含参构造函数 可以自由选择使用公平锁还是非公平锁实现。
- 一旦初始化就不能更改。因为RTL的属性Sync使用final修饰,一旦初始化就不可修改引用。
/** Synchronizer providing all implementation mechanics */
private final Sync sync; // 私有变量使用final修饰
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}