Java除了使用关键字synchronized外,还可以使用ReentrantLock实现独占锁的功能。而且ReentrantLock相比synchronized而言功能更加丰富,使用起来更为灵活,也更适合复杂的并发场景。这篇文章主要是从源码的角度来分析一下ReentrantLock。
ReentrantLock
一、加锁
代码如下:
ReentrantLock reentrantLock = new ReentrantLock();
reentrantLock.lock();
先看创建ReentrantLock对象,ReentrantLock有公平锁和非公平锁两种方式,默认是非公平锁。
public ReentrantLock() {
sync = new NonfairSync();
}
进入NonfairSync类的lock方法。
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
通过CAS将state由0设置为1,同一时刻多线程只能有一个设置成功,并将线程设置为当前线程。失败的会进入acquire(1)方法。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
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;
}
tryAcquire方法会尝试再次获取一次,如果成功则返回true。否则会判断当前线程是否等于ReentrantLock中的线程,如果相等,代表已获得锁,需要再次进入,相当于重入锁,并把数值+1。
如果获取失败,会通过acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法尝试加入到等待队列。
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;
//如果已存在前置节点,设置pred为新增节点的前置节点
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//如果不存在前置节点,进入enq方法
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
//如果tail等于null,新创建head和tail节点
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
//设置head为新增节点的前置节点,并设置tail为新增节点
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
接着进入acquireQueued方法
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//获取当前节点的前置节点
final Node p = node.predecessor();
//如果前置节点等于head,尝试再次获取
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//如果获取失败,执行shouldParkAfterFailedAcquire方法
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
首先会获取当前节点的前置节点,判断前置节点是否为head节点。比如有线程A和线程B,如果线程A执行lock方法获取到锁,则B线程会创建Node对象,B线程Node对象的前置节点是head,之后尝试获取锁。如果获取成功,将自己设置为Head节点。如果失败,会进入shouldParkAfterFailedAcquire(p, node)方法。
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
//如果ws等于Node.SIGNAL(-1),则返回true应该等待
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//初始节点ws等于0,将0置为Node.SIGNAL(-1)状态,等待获取
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
Node节点有四种状态,1-取消,0-初始化,-1 - 等待, -2 - Condition状态
第一次进入shouldParkAfterFailedAcquire方法后,会将node节点的waitStatus状态由0置为-1。返回false。之后外面会for循环再次进入,当node节点状态为-1时,返回true。之后进入parkAndCheckInterrupt方法。
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
parkAndCheckInterrupt方法比较简单,通过LockSupport.park方法阻塞线程。
二、解锁
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
线程A获取锁之后,调用unlock方法。会调用tryRelease方法。
protected final boolean tryRelease(int releases) {
//获取ReentrantLock的state数值,减去传入的1
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//为0则代表释放锁
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
//否则设置state数值,也就是我们lock几次,就要调用unlock几次
setState(c);
return free;
}
tryRelease方法会获取ReentrantLock的state数值,减去传入的1。如果减为0,则代表释放锁。否则设置state数值,也就是我们lock几次,就要调用unlock几次。释放后会返回true。调用unparkSuccessor方法。
private void unparkSuccessor(Node node) {
//获取head节点状态,如果为-1(等待),置为初始化
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
//获取head节点的下一节点
Node s = node.next;
//如果没有,或者状态为取消,则倒序找到不为取消的最开始等待的节点
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
//将之前阻塞的线程唤醒
LockSupport.unpark(s.thread);
}
之前被阻塞的线程B会继续执行
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
会继续执行
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//获取当前节点的前置节点
final Node p = node.predecessor();
//如果前置节点等于head,尝试再次获取
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//如果获取失败,执行shouldParkAfterFailedAcquire方法
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
这次执行tryAcquire会成功,之后将node节点设置为head节点。
private void cancelAcquire(Node node) {
// Ignore if node doesn't exist
if (node == null)
return;
node.thread = null;
// Skip cancelled predecessors
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
// predNext is the apparent node to unsplice. CASes below will
// fail if not, in which case, we lost race vs another cancel
// or signal, so no further action is necessary.
Node predNext = pred.next;
// Can use unconditional write instead of CAS here.
// After this atomic step, other Nodes can skip past us.
// Before, we are free of interference from other threads.
node.waitStatus = Node.CANCELLED;
// If we are the tail, remove ourselves.
if (node == tail && compareAndSetTail(node, pred)) {
compareAndSetNext(pred, predNext, null);
} else {
// If successor needs signal, try to set pred's next-link
// so it will get one. Otherwise wake it up to propagate.
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
unparkSuccessor(node);
}
node.next = node; // help GC
}
}
Condition
Condition是ReentrantLock中的一种条件控制对象,类似于Object中的wait和notify,但是比wait/notify更加方便。一个Object对象只能有一个控制条件,就是wait/notify。而ReentrantLock可以创建多个Condition条件等待,满足多条件等待与唤醒。
先写一个例子熟悉一下Condition的使用,
public class MoreThreadExecute {
private static ReentrantLock lock = new ReentrantLock();
private static Condition a = lock.newCondition();
private static Condition b = lock.newCondition();
private static Condition c = lock.newCondition();
private static AtomicInteger t = new AtomicInteger(0);
private static final int THREADS = 3;
public static void main(String[] args) {
MoreThreadExecute lockTest = new MoreThreadExecute();
ExecutorService pool = Executors.newFixedThreadPool(THREADS);
pool.execute(lockTest.new PrintTask("B thread",b, c, "B", 1));
pool.execute(lockTest.new PrintTask("C thread",c, a, "C", 2));
pool.execute(lockTest.new PrintTask("A thread",a, b, "A", 0));
}
class PrintTask implements Runnable {
//本线程的条件
private Condition me;
//下个线程的条件
private Condition next;
private String message;
private int index;
private String name;
public PrintTask(String name,Condition me, Condition next, String message, int index) {
this.name = name;
this.me = me;
this.next = next;
this.message = message;
this.index = index;
}
@Override
public void run() {
while(true) {
if (t.get() >6){
break;
}
try {
lock.lock();
while(t.get() % THREADS != index) {
try {
System.out.println(name+"invoked, but not me");
//不到本线程的时机,则等待
me.await();
} catch (InterruptedException e) {
if (Thread.interrupted()) {
e.printStackTrace();
}
}
}
System.out.println(message);
t.incrementAndGet();
//通知其他线程重新获取锁,该他们执行了
next.signalAll();
}finally {
lock.unlock();
}
}
}
}
}
执行的就是打印A、B、C顺序执行。
下面开始Condition源码解析:
首先我们需要先创建ReentrantLock对象,接着调用lock.newCondition()方法创建Condition。
public Condition newCondition() {
return sync.newCondition();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
ConditionObject类是AQS里的一个内部类。
接着我们看lock.await()方法。
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//创建条件队列
Node node = addConditionWaiter();
//释放全部资源
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
1、会先通过addConditionWaiter方法创建条件等待队列
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
创建一个Node节点,waitState为-2(条件等待)
2、调用fullyRelease方法,释放占用的资源。
final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
这里为什么要释放掉全部资源?
因为我们调用Object.wait或者notify方法的前提一定是通过synchronized获取到了锁才可以。这里如果我们要await等待其他条件触发执行,那么我们需要把当前获得的锁全部释放才可以,如果失败,则会报IllegalMonitorStateException异常。
3、通过isOnSyncQueue方法判断是否在同步队列中。
final boolean isOnSyncQueue(Node node) {
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
if (node.next != null) // If has successor, it must be on queue
return true;
/*
* node.prev can be non-null, but not yet on queue because
* the CAS to place it on queue can fail. So we have to
* traverse from tail to make sure it actually made it. It
* will always be near the tail in calls to this method, and
* unless the CAS failed (which is unlikely), it will be
* there, so we hardly ever traverse much.
*/
return findNodeFromTail(node);
}
AQS中的Node分为两种队列,一种是需要获取锁的同步队列,另一个是Condition创建的条件队列。一个Node只能在一种队列中,如果在条件队列中,就不会在同步队列中。第一次调用await方法后,肯定不会在同步队列中,所以返回false。之后调用LockSupport.park(this);方法阻塞等待唤醒。
4、假设这时另一线程调用signalAll方法进行唤醒。
public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);
}
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();
}
首先会判断当前线程是否获取到锁,如果没有会抛出IllegalMonitorStateException异常。接着会调用doSignalAll方法。
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
方法会获取新创建的Condition的Node节点,执行transferForSignal方法,将Node节点的状态由-2(等待条件)置为0,并将该Node节点加入到同步队列中。并释放前一节点的线程。继续回到之前阻塞的地方
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//创建条件队列
Node node = addConditionWaiter();
//释放全部资源
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
会再次进入isOnSyncQueue方法,判断当前节点是否进入同步队列。
final boolean isOnSyncQueue(Node node) {
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
if (node.next != null) // If has successor, it must be on queue
return true;
return findNodeFromTail(node);
}
private boolean findNodeFromTail(Node node) {
Node t = tail;
for (;;) {
if (t == node)
return true;
if (t == null)
return false;
t = t.prev;
}
}
由之前的signalAll方法,已将队列waitState由-2变为-1,并将node由等待队列加入到同步队列。这里可以从findNodeFromTail方法找到同步队列中包含当前Node,所以返回true。不满足while循环条件。
6、进入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);
}
}
尝试获取方法和lock加锁等待方法一样。node节点尝试获取。如果未获取到锁,则继续阻塞等待释放。如果获取了则继续向下执行。
7、接着会执行if (node.nextWaiter != null)判断。
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
private void unlinkCancelledWaiters() {
Node t = firstWaiter;
Node trail = null;
while (t != null) {
Node next = t.nextWaiter;
if (t.waitStatus != Node.CONDITION) {
t.nextWaiter = null;
if (trail == null)
firstWaiter = next;
else
trail.nextWaiter = next;
if (next == null)
lastWaiter = trail;
}
else
trail = t;
t = next;
}
}
unlinkCancelledWaiters方法清除条件队列上节点状态不为 CONDITION 的节点。
unlinkCancelledWaiters方法其实没有什么用,因为如果await超时或者线程中断时,会尝试将该node节点的waitState状态置为0,并加入到同步队列。
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
//假设设立被其他线程调用interrupt阻断或者被唤醒,进入checkInterruptWhileWaiting方法
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
//被阻断进入transferAfterCancelledWait
final boolean transferAfterCancelledWait(Node node) {
//会将该node节点置为0
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
enq(node);
return true;
}
/*
* If we lost out to a signal(), then we can't proceed
* until it finishes its enq(). Cancelling during an
* incomplete transfer is both rare and transient, so just
* spin.
*/
while (!isOnSyncQueue(node))
Thread.yield();
return false;
}
即使这里不调用unlinkCancelledWaiters方法,在执行signalAll唤醒方法时,也不会将这些节点加入同步队列中。只是为了清理一下等待队列上节点状态不为 CONDITION 的节点。
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
总结:
Condition 自己也维护了一个队列,该队列的作用是维护一个等待 signal 信号的队列,两个队列的作用是不同,事实上,每个线程也仅仅会同时存在以上两个队列中的一个。将设有线程 1、2,下面来具体过程。
线程1:
-
线程1 调用 reentrantLock.lock时,持有锁。
-
线程1 调用 await 方法,进入[条件等待队列],同时释放锁。
-
线程1 获取到线程2 signal 信号,从 [条件等待队列] 进入 [同步等待队列]。
线程2:
-
线程2 调用 reentrantLock.lock时,由于锁被线程1 持有,进入 [同步等待队列]。
-
由于线程1 释放锁,线程2 从 [同步等待队列] 移除,获取到锁。线程2 调用 signal 方法,导致线程 1 被唤醒。
-
线程2 调用 reentrantLock.unlock 。线程1 获取锁,继续循环。
条件等待队列
条件等待队列,指的是 Condition 内部自己维护的一个队列,不同于 AQS 的[同步等待队列]。它具有以下特点:
- 要加入[条件等待队列]的节点,不能在 [同步等待队列]。
- 从 [条件等待队列] 移除的节点,会进入[同步等待队列]。
- 一个锁对象只能有一个[同步等待队列],但可以有多个[条件等待队列]。