ReentrantLock
简介ReentrantLock
是可重入锁的实现,可重入锁的含义是:如果已经拥有锁的线程再次获取锁时会立即响应成功,这点可以使用isHeldByCurrentThread
和getHoldCount
方法来检验。ReentrantLock
可重入互斥锁具有同synchronized
的隐式监视器锁相同的基本行为和语义,但是其更具有扩展能力。
ReentrantLock
提供公平锁和非公平锁的特性,如果设置为公平锁,那么锁倾向于访问等待时间最长的线程;如果是非公平锁,那便不会保证任何特定的访问顺序。公平锁会导致整体的吞吐量降低,不过却可以在最少的时间差内获取到锁和保证锁饥饿。不过请注意,锁是公平并不能保证线程调度的公平性。
ReentrantLock
使用典型用法如下(这里以lock
为例,当然也可以用tryLock
方法来获取锁):
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
ReentrantLock
源码剖析源码中涉及AQS模板类的调用,可选择先查看《JUC之AQS解读》这篇文章的介绍。
对于同步状态,依然是继承与AQS来实现的,考虑到有公平锁和非公平锁的特性,其又做了一层抽象,提供一些共用方法,如:
对于
lock
的加锁过程则交予具体的锁类型同步器去实现
其中获取非公平锁方式放在这个抽象层的原因是为了ReentrantLock
在默认非公平性下调用:
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
抽象同步器的源码如下:
abstract static class Sync extends AbstractQueuedSynchronizer {
abstract void lock();
final boolean nonfairTryAcquire(int acquires) {
// 省略
}
protected final boolean tryRelease(int releases) {
// 省略
}
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
ReentrantLock
公平锁和非公平锁的特性可以通过选择如下构造函数来设定:
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
如果直接使用默认的无参构造函数,那么是用的非公平锁:
public ReentrantLock() {
sync = new NonfairSync();
}
公平锁和非公平锁的同步状态器源码如下:
static final class NonfairSync extends Sync {
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThreadcurrentThread(Thread.());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
static final class FairSync extends Sync {
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;
}
}
同时结合前面的抽象同步器代码,可以总结出区别在于:
compareAndSetState(0, 1)
,如果抢占成功则直接加上锁。state==0
的条件下多了一层判断!hasQueuedPredecessors()
来保证公平有序。hasQueuedPredecessors
的作用是判断是否有其他线程先于当前线程进行等待,避免非公平抢占。其源码如下,通过判断是否有等待节点来告知:
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
对于可重入性,就是在判断到当前线程持有锁的时候,再将状态值进行加法:
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
对于可重入的次数而言,由于继承的是AQS并使用其state
字段,所以最大值只能到Integer.MAX_VALUE
,超出此限制会抛出Error
。
/**
* The synchronization state.
*/
private volatile int state;
回到抽象同步器的tryRelease
方法,这是对锁释放的一个操作,其完成的功能有如下3步:
state - releases
; 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;
}
根据AQS模板类的使用,会根据如下调用流程走完锁释放流程:
ReentrantLock.unlock -> aqs.release -> ReentrantLock.tryRelease