一、ReentrantLock简介
ReentrantLock可重入锁,全名java.util.concurrent.locks.ReentrantLock,相当于是个最基础版本的Lock的实现,针对公平锁和非公平锁,ReentrantLock都有实现。
二、ReentrantLock特性
1、可轮询锁和定时锁
可以通过调用trylock方法,查询锁的状态,如果锁已经被其他线程持有,则不会一直阻塞下去,避免死锁。
2、公平锁和非公平锁
公平锁是按照发出请求的顺序获取锁,不允许插队;而非公平锁允许插队,二者各有优缺点,ReentradntLock都有实现。
公平锁的作用就是严格按照线程启动的顺序来执行的,不允许其他线程插队执行的;而非公平锁是允许插队的,但由于可以减少线程恢复和挂起操作,总体执行效率更高。
3、可中断锁
lockInterruptibly方法能够在获取锁的同时保持对中断的响应,因此无需创建其它类型的不可中断阻塞操作。
三、ReentrantLock原理
ReentrantLock的架构相对简单,主要包括一个Sync的内部抽象类以及Sync抽象类的两个实现类。Sync继承自AQS,Sync的两个实现类分别是NonfairSync和FairSync,即非公平锁和公平锁。下面分别针对非公平锁和公平锁讲一下ReentrantLock的原理。
1、非公平锁的lock
非公平锁lock方法源码如下:
// 非公平锁的lock方法
// java.util.concurrent.locks.ReentrantLock.NonfairSync#lock
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer#acquire
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
调用过程整理,总共分为两步:
第一步,直接尝试CAS抢占锁,如果抢占成功,返回成功,如果抢占失败,则进行第二步;
第二步,调用acquire方法,这个方法内做了两件事,第一件事,先调用tryAcquire方法尝试抢占锁,如果失败则第二件事,即先调用addWaiter方法将线程节点加入同步队列,然后进入acquireQueued方法尝试抢占锁。
下面分别讲这三个方法,tryAcquire、addWaiter和acquireQueued。
tryAcquire方法内,先判断当前锁是否已被抢占,如果未被抢占,那么就CAS尝试抢占锁,如果抢占成功,那么就设置当前线程为已抢占锁的线程;如果锁还未被抢占,那么判断当前线程是不是已获得锁的线程,如果是,那么就更新同步状态位,否则抢占失败。tryAcquire方法源码如下:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
// java.util.concurrent.locks.ReentrantLock.Sync#nonfairTryAcquire
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;
}
addWaiter方法内,先判断当前队列是否为空,如果为空,则直接CAS设置当前节点为头结点;如果队列不为空,则CAS设置当前节点为尾节点。addWaiter方法源码如下:
// java.util.concurrent.locks.AbstractQueuedSynchronizer#addWaiter
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
acquireQueued方法内,在一个死循环内,不断判断当前节点的前驱节点是否是头结点,如果是,则表示当前节点可抢占锁,会调用tryAcquire方法尝试抢占锁。acquireQueued方法源码如下:
// java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireQueued
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
2、公平锁的lock
公平锁与非公平锁的实现有两个差别,一是不会直接抢占,而是只调用acquire方法;二是tryAcquire方法实现有差别,上面讲到非公平锁在抢占锁时,如果当前无线程占锁,那么就直接CAS抢占锁,而非公平锁需要先判断当前节点是否无前驱节点,如果没有前驱节点才可以抢占锁,这是为了保证FIFO。源码如下:
// 公平锁lock,其中,acquire方法与非公平锁是同一个
final void lock() {
acquire(1);
}
// java.util.concurrent.locks.ReentrantLock.FairSync#tryAcquire
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;
}
3、unlock
unlock是不区分公平锁和非公平锁的,unlock的时候做两件事,一是同步状态更新并setExclusiveOwnerThread(null)设置当前持有锁的线程为null,二是唤醒队列中的后续节点抢占,unlock锁源码如下:
public void unlock() {
sync.release(1);
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer.release()
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
// java.util.concurrent.locks.ReentrantLock.Sync#tryRelease
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;
}
注:
本文中所有源码均为jdk1.7.0_79版本。