ReentrantLock可重入锁,是一种递归无阻塞的同步机制。它可以等同与synchornized实现同步。并且提供了比synchornized更为灵活的加锁机制。
ReentrantLock提供了公平锁FairSync和非公平锁NonfairSync两种机制。
- 公平锁,锁的获取是有序的
- 非公平锁,获取锁不是有序的,但效率比公平锁高,在多线程下,吞吐量较高。
ReentrantLock实现了Lock接口,内部基于Sync实现。
Sync类
Sync是ReentrantLock中的内部静态类,它继承了AbstractQueuedSynchronizer类,它使用AbstractQueuedSynchronizer类中state字段表示当前锁的持有数量,从而实现重入功能。其源码如下:
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
//加锁
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
//非公平锁获取锁
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//当前持有锁的数量
int c = getState();
//如果为0,说明是空闲状态
if (c == 0) {
if (compareAndSetState(0, acquires)) {
//将锁设置为当前线程所有。
setExclusiveOwnerThread(current);
return true;
}
}
//不是空闲状态,判断当前线程是否是持有锁的线程,如果是,线程重入,持有锁的数量 + acquires
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;
}
//释放锁
protected final boolean tryRelease(int releases) {
//减掉持有锁数量
int c = getState() - releases;
//当前线程不是持有锁线程,抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//如果减去持有锁数量后,同步状态为0,说明为空闲状态,设置为空闲状态
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
//设置持有数量
setState(c);
//返回是否释放成功
return free;
}
//当前线程是否是以独占锁的方式运行的
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();
}
//Condition条件
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
//当前持有锁的线程
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
//当前持有锁的数量
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
//是否有锁
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
此段代码中重点关注非公平锁获取方式以及释放锁方法,非公平锁获取方式主要流程如下:
- 首先判断线程同步状态,如果为0,说明是空闲状态,就将锁设置为当前线程所有。
- 如果部位0,判断当前线程是否是持有锁的线程,如果是,则是线程重入,将持有锁的数量 + acquires,实现线程重入的特性。
- 如果获取到锁,返回true,否则返回false.
释放锁流程如下:
- 首先将state减去release;
- 判断当前线程是否是持有锁的线程,如果不是则抛出异常。
- 判断state是否为0,为0说明锁为空闲状态,将锁持有的线程设置为努力了,其他线程就可以获取同步状态了。
- 同步状态释放成功,则返回true,否则返回false.
NonfairSync类
NonfairSync 是 ReentrantLock 的内部静态类,实现 Sync 抽象类,是非公平锁实现类。其源码如下:
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
//实现Sync类的lock方法
final void lock() {
//CAS ,如果设置状态成功,将当前线程设置为持有锁线程
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//失败,尝试获取锁,此方法在AbstractQueuedSynchronizer中。
acquire(1);
}
//重写AbstractQueuedSynchronizer类的tryAcquire方法
protected final boolean tryAcquire(int acquires) {
//非公平锁获取
return nonfairTryAcquire(acquires);
}
}
FairSync类
NonfairSync 是 ReentrantLock 的内部静态类,实现 Sync 抽象类,是公平锁实现类。其源码如下:
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.
*/
//重写AQS的方法,尝试获取公平锁
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;
}
}
公平锁获取方式比非公平锁获取方式多了一个hasQueuedPredecessors方法,此方法在AQS类中,主要作用是判断当前线程节点是否有前置等待获取锁的线程节点。其源码如下:
//该方法主要是判断当前线程是否位于 CLH 同步队列中的第一个。如果是则返回 true ,否则返回 false 。
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
ReentrantLock
ReentrantLock的实现方法,基本上是对Syncnn内部类的调用,其主要方法包括以下几个:
- ReentrantLock()无参构造,创建一个非公平锁的重入锁。
- ReentrantLock(boolean fair) 根据fair创建一个重入锁,fair为true为公平锁,false为非公平锁。
- lock() 获取锁。
- tryLock()尝试获取锁。
- unLock()释放锁。
- newCondition()条件。
源码:
//创建非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
//根据fair创建公平锁或非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
//获取锁,立即返回
public void lock() {
sync.lock();
}
//尝试获取锁,实现时,希望快速获取锁,所以即使fair= true即为公平锁的情况下依然使用非公平锁获取的实现。
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
//带有超时 的获取锁,此方法按照fair实现的方式获取锁。
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
//释放锁
public void unlock() {
sync.release(1);
}
//Condition条件
final ConditionObject newCondition() {
return new ConditionObject();
}
注:tryLock()方法在使用时,即使时公平锁的实现,也会调用非公平锁的方法快速获取锁。这也是nonfairTyrAcquire()方法在Sync写在Sync而非NonfairSync中的原因。
ReentrantLock和Synchronized的区别
ReentrantLoc和synchronized两者都能够实现线程通过,两者之间的区别主要在以下几个方面:
- 与
synchronized
相比,ReentrantLock提供了更多,更加全面的功能,具备更强的扩展性。例如:时间锁等候,可中断锁等候,锁投票。- ReentrantLock 还提供了条件 Condition ,对线程的等待、唤醒操作更加详细和灵活,所以在多个条件变量和高度竞争锁的地方,ReentrantLock 更加适合。
- ReentrantLock 提供了可轮询的锁请求。它会尝试着去获取锁,如果成功则继续,否则可以等到下次运行时处理,而
synchronized
则一旦进入锁请求要么成功要么阻塞,所以相比synchronized
而言,ReentrantLock会不容易产生死锁些。- ReentrantLock 支持更加灵活的同步代码块,但是使用
synchronized
时,只能在同一个synchronized
块结构中获取和释放。注意,ReentrantLock 的锁释放一定要在finally
中处理,否则可能会产生严重的后果。- ReentrantLock 支持中断处理,且性能较
synchronized
会好些。