ReentrantLock的概念和结构
概念
- ReentrantLock基于AQS,在并发编程中可以实现公平锁和非公平锁来对共享资源进行同步,同时,和synchronized一样,ReentrantLock支持可重入,除此之外,ReetrantLock在调度上更加灵活,支持更多丰富的功能。
结构
- ReetrantLock实现Lock接口,同时里面包含三个内部类,Sync、FairSync、NonfairSync;
- Sync继承AQS,除了lock()、readObject()方法其他的都用final修饰,防止子类修改,是对AQS的封装和拓展,本身实现已完全可靠、不希望被外部破坏;
- FairSync、NonfairSync继承Sync,都只重写Sync的lock()、AQS的tryAcquire()方法;
公平锁与非公平锁
构造函数
- 默认采用非公平的方式;
- true是公平、false是非公平
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
说明
- FairSync:只要资源被其他线程占用,该线程就会添加到sync queue中的尾部,而不是先尝试获取资源;
- NonFairSync:每一次都尝试获取资源,如果此时资源恰好被释放,则会被当前线程获取,造成不公平现象,当获取不成功,再加入队列尾部。
Lock类的常见方法
lock.lock()
- 用于获取锁,如果当前锁被其他线程占用,将会等待直到获取为止。
流程
- 根据传入具体的实体类,判断调用FairSync.lock() 或者 NonfairSync.lock();
- 通过CAS尝试获取锁,失败调用AQS的acquire();
- AQS的acquire()会首先调用tryAcquire()、对应有不同的实体类重写FairSync.tryAcquire() 或者 NonfairSync.tryAcquire();
- FairSync.tryAcquire() 会直接返回Sync的nonfairTryAcquire(),里面获取state,如果为0,进行CAS尝试锁,不为0,如果当前线程就是占有锁的线程,进行重入,使用nextc记录重入次数;
- 如果tryAcquire()失败,调用 addWaiter() 将当前线程封装成Node,加入队列中;acquireQueued() 将队列的head之后的一个节点在自旋CAS获取锁,其他线程都被挂起。挂起的线程等待执行release()释放锁资源。
以NonfairSync为例的详解
- sync通过构造函数传入初始化,判断是new FairSync 还是 new NonfairSync
public void lock() {
sync.lock();
}
NonfairSync的lock方法
- 首先尝试一次对锁的获取,如果CAS成功,那么当前线程成功获得锁,同时将当前线程置为独占线程,并返回true;
- 否则,调用AQS的acquire方法;
final void lock() {
// state为0,表示锁状态为空闲
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
AQS的acquire方法
- 首先会调用tryAcquire()、子类NonfairSync重写了此方法;
- 如果失败调用addWaiter(),将调用此方法的线程封装成为一个结点并放入Sync queue;
- 调用acquireQueued(),将在sync queue中的结点不断尝试获取资源,如果成功返回true,失败返回false。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
NonfairSync.tryAcquire() 直接返回 Sync的nonfairTryAcquire()
- 获取state,该值由AQS维护;
- 当state为0,表示锁状态空闲,便可以通过一次CAS来原子地更改state,如果state更改成功,则代表获取了锁,将当前线程置为独占线程,并返回true;
- 当state不为0,说明锁被占用,判断当前线程是否为独占线程(占用锁的线程);如果是,则代表重入,使用nextc记录重入次数;
补充:
判断state是否小于0的意义?
- state使用int类型存储,最大的有符号数为2147483647,一旦超出便会溢出变为负数;也可以理解为,ReetrantLock允许重入的最大次数其实就是2147483647;
可重入性?
- 一个线程可以不用释放而重复获取一个锁n次,只是在释放的时候也需要相应的释放n次。
// NonfairSync
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
// Sync
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;
}
补充:fairSync.lock()、tryAcquire()的区别
- 判断锁是否空闲,如果空闲,并不是直接尝试通过CAS获取锁,而是需要判断是否存在前置等待结点。如果不存在,说明队列中确实已经轮到当前线程尝试获取锁,体现了公平性。
final void lock() {
acquire(1);
}
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;
}
lock.tryLock()
- 尝试获取锁、tryLock()的操作都是非公平的,不管传入的是公平还是非公平;
- nonfairTryAcquire()具体详解上面讲lock()的时候已经涉及到了。
// ReentrantLock
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
// Sync
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;
}
lock.unlock()
- 释放锁,tryRelease()需要完全释放;如果遇到重入的情况,可能会返回释放多次。
// ReentrantLock
public void unlock() {
sync.release(1);
}
// AQS
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
// Syn
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;
}
lock.lockInterruptibly()
用于获取锁,如果当前线程在等待锁的过程中被中断,会退出等待并抛出异常。
lock.newCondition()
创建condition对象,相当于Object类的wait、notify方法