ReentrantLock源码系列
ReentrantLock源码解读(1)——CAS
ReentrantLock源码解读(2)——ReentrantLock源码与AQS
ReentrantLock源码解读(3)——Condition
AQS(AbstractQueuedSynchronizer),它是java中可重入锁和其他同步组件的基础框架。
先看一段ReentrantLock中的部分源码码
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
ReentrantLock的构造函数。核心成员sync,根据传入的boolean决定是公平还是非公平。
static final class NonfairSync extends Sync {
...
static final class FairSync extends Sync {
...
abstract static class Sync extends AbstractQueuedSynchronizer {
不管是公平锁还是非公平锁,都是继承Sync,Sync继承AbstractQueuedSynchronizer,而关于加解锁的核心操作,都在AbstractQueuedSynchronizer中,所以需要先阅读AQS源码。
- 初看AQS
先看看AQS成员变量
/**
* Head of the wait queue, lazily initialized. Except for
* initialization, it is modified only via method setHead. Note:
* If head exists, its waitStatus is guaranteed not to be
* CANCELLED.
*/
private transient volatile Node head;
/**
* Tail of the wait queue, lazily initialized. Modified only via
* method enq to add new wait node.
*/
private transient volatile Node tail;
/**
* The synchronization state.
*/
private volatile int state;
/**
* The number of nanoseconds for which it is faster to spin
* rather than to use timed park. A rough estimate suffices
* to improve responsiveness with very short timeouts.
*/
static final long spinForTimeoutThreshold = 1000L;
/**
* Setup to support compareAndSet. We need to natively implement
* this here: For the sake of permitting future enhancements, we
* cannot explicitly subclass AtomicInteger, which would be
* efficient and useful otherwise. So, as the lesser of evils, we
* natively implement using hotspot intrinsics API. And while we
* are at it, we do the same for other CASable fields (which could
* otherwise be done with atomic field updaters).
*/
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;
static {
try {
stateOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
headOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
waitStatusOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("waitStatus"));
nextOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("next"));
} catch (Exception ex) { throw new Error(ex); }
}
head:等待队列的头部,tail:等待队列的尾部,state同步状态,spinForTimeoutThreshold一个1ms的超时时间,还不知道具体做什么用。后面几个是CAS操作所需要的,上一篇讲过。
看到这里有个疑惑,这个等待队列只有头和尾,那它到底是个什么结构呢?看Node代码。
/** Marker to indicate a node is waiting in shared mode */
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
static final Node EXCLUSIVE = null;
两个标记,共享模式等待标记,和独占模式等待标记。分别对应这共享锁和排他锁
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;
volatile int waitStatus;
枚举了4个等待状态,CANCELLED:线程取消等待;SIGNAL:需要唤醒下一个线程;CONDITION:有condition等待;PROPAGATE:传播,暂时不知道具体是什么。
volatile Node prev;
volatile Node next;
/**
* The thread that enqueued this node. Initialized on
* construction and nulled out after use.
*/
volatile Thread thread;
prev指向上一个node,next指向下一个node,thread就是node所持有线程,也就是队列中排队的线程,也就是没有抢到锁而等待的线程。
/**
* Link to next node waiting on condition, or the special
* value SHARED. Because condition queues are accessed only
* when holding in exclusive mode, we just need a simple
* linked queue to hold nodes while they are waiting on
* conditions. They are then transferred to the queue to
* re-acquire. And because conditions can only be exclusive,
* we save a field by using special value to indicate shared
* mode.
*/
Node nextWaiter;
/**
* Returns true if node is waiting in shared mode.
*/
final boolean isShared() {
return nextWaiter == SHARED;
}
这是一个共享锁判断,nextWaiter还与condition使用有关。
/**
* Returns previous node, or throws NullPointerException if null.
* Use when predecessor cannot be null. The null check could
* be elided, but is present to help the VM.
*
* @return the predecessor of this node
*/
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
简单地获取上一个节点的的引用。
看完node源码可以大致知道,这个队列就是等待线程队列,它是一个双向队列,队列中持有的核心值就是thread,每个节点还记录thread等待状态以及共享锁、独占锁标志。
这时候想看看通过一个等待队列和一个状态位如何实现锁的,但AQS类方法太多,不知道如何下手。读源码,方法太多不知道如何下手的时候,两个办法:从public方法下手;从调用的地方下手。AQS类public方法也很多,这个时候采取后者。
- ReentrantLock源码看AQS
首先,ReentrantLock源码中的NonfairSync和FairSync策略模式,先看从非公平锁看起。
lock()方法
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
compareAndSetState是AQS类的方法。
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
这里就是一个CAS操作,期望值是0,要更新为1。偏移量为stateOffset,所以它是操作state的,state默认是0,加锁时期望更新为1,所以0,1分别对应已加锁,和未加锁的状态。
回到lock()方法,这个CAS操作成功,说明加锁成功。然后看,加锁成功的后的操作setExclusiveOwnerThread()
public abstract class AbstractOwnableSynchronizer
implements java.io.Serializable {
...
/**
* The current owner of exclusive mode synchronization.
*/
private transient Thread exclusiveOwnerThread;
/**
* Sets the thread that currently owns exclusive access.
* A {@code null} argument indicates that no thread owns access.
* This method does not otherwise impose any synchronization or
* {@code volatile} field accesses.
* @param thread the owner thread
*/
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
...
}
这个方法来源于AbstractOwnableSynchronizer,而AQS也继承了这个类,这个类只有一个成员变量exclusiveOwnerThread,用来记录获取到独占锁的线程,所以setExclusiveOwnerThread(Thread.currentThread());是把当前线程set为获取独占锁的线程。
接下来看加锁失败后的操作
/**
* Acquires in exclusive mode, ignoring interrupts. Implemented
* by invoking at least once {@link #tryAcquire},
* returning on success. Otherwise the thread is queued, possibly
* repeatedly blocking and unblocking, invoking {@link
* #tryAcquire} until success. This method can be used
* to implement method {@link Lock#lock}.
*
* @param arg the acquire argument. This value is conveyed to
* {@link #tryAcquire} but is otherwise uninterpreted and
* can represent anything you like.
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
这也是AQS里面的一个方法,翻译一下说明,以独占模式获取,忽略中断。传过来的参数是1,这个获取应该也是获取锁。继续看方法体里面调用的几个方法
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
初看一眼,可能会有点疑惑,它抛出一个不支持方法异常,说明这个方法要被它的子类重写,但它并没有设置成虚方法,说明它要求使用这个方法的子类必须重写这个方法,它这里只起一个申明作用。回到NonfairSync类看这个方法。
static final class NonfairSync extends Sync {
...
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
// 分割线---
abstract static class Sync extends AbstractQueuedSynchronizer {
/**
* 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();
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;
}
}
万万没想到,它的最终实现在Sync这个类里面。这个方法也是尝试获得锁,先看锁有没有被其他线程占用,没有就CAS尝试加锁。然后再看当前线程是否锁的持有线程,是的话,这个state再加个1,这一步就是锁的重入,多次加锁。所以这个state不仅有0、1,还有其他值,表示被加锁的次数,看到这里,可以知道,如果我们使用ReentrantLock多次加锁,必须释放锁同样次数。
再看acquireQueued(addWaiter(Node.EXCLUSIVE), arg),这里注意一点就是如果tryAcquire(arg)尝试获得锁成功,就不会往下走。
/**
* Creates and enqueues node for current thread and given mode.
*
* @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
* @return the new node
*/
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;
}
/**
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
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;
}
}
}
}
/**
* CAS head field. Used only by enq.
*/
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
/**
* CAS tail field. Used only by enq.
*/
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
这是addWaiter涉及到的所有操作,addWaiter中的操作其实只是一个快速操作,或者说预操作,因为开始pred为null,直接进enq(),其次compareAndSetTail可能失败。但是它也有意义就是最好情况下,直接将node加入到队列:pred指向tail,tail指向node,node.prev指向pred,pred.next指向node。
在enq()中操作其实一样,只不过多了初始化的操作。注意初始化操作是new Node()作为head。贴一张百度找来的队列的结构图。
这个方法最后return的是addWaiter方法里面创建的包含当前线程引用的node。
再看acquireQueued方法
/**
* Acquires in exclusive uninterruptible mode for thread already in
* queue. Used by condition wait methods as well as acquire.
*
* @param node the node
* @param arg the acquire argument
* @return {@code true} if interrupted while waiting
*/
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);
}
}
看到这个for死循环,结合CAS操作,这里大概就是循环不断尝试获得锁,也就是锁自旋。在这个自旋里面有两个判断,第一个判断,node的上一个节点为head头节点,而且tryAcquire()尝试加锁成功。这个时候返回的interrupted(中断状态)为false。
第二个判断,接着看代码。
/**
* Checks and updates status for a node that failed to acquire.
* Returns true if thread should block. This is the main signal
* control in all acquire loops. Requires that pred == node.prev.
*
* @param pred node's predecessor holding status
* @param node the node
* @return {@code true} if thread should block
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
/**
* CAS waitStatus field of a node.
*/
private static final boolean compareAndSetWaitStatus(Node node,
int expect,
int update) {
return unsafe.compareAndSwapInt(node, waitStatusOffset,
expect, update);
}
shouldParkAfterFailedAcquire这个方法,主要操作pred的waitstatus,pred是前驱节点而不是当前节点。将它的值设置为SIGNAL,只有已经为SIGNAL了才返回true,其他情况返回false。并且如果pred.waitstatus > 0(CANCELLED),则跳过该前驱节点,并且一直循环往前跳,直到出现没有CANCELLED的节点。
再看parkAndCheckInterrupt()
/**
* Convenience method to park and then check if interrupted
*
* @return {@code true} if interrupted
*/
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
通过 LockSupport.park(this),传入this,隔离不同的ReentrantLock。LockSupport原理,内部也是通过unsafe类实现,这里不多说。Thread.interrupted()不仅检验当前线程是否有中断标志,并且会清除中断。
到此,加锁失败的线程已经阻塞并在等待队列了。什么时候会被唤醒,调用LockSupport.unpark(),还有一种方法,调用Thread类的interrupt()方法,产生一个中断。 LockSupport.park()是会响应这个中断的。
在acquireQueued()方法中,有个try finally代码块。调用cancelAcquire()来取消获取锁这个操作,看它如何实现。
/**
* Cancels an ongoing attempt to acquire.
*
* @param node the node
*/
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
}
}
这个方法里先skip node前面CANCELLED状态的节点。这个方法中pred为node的前驱节点(实际上是非CANCELLED状态的前驱),predNext为前驱节点的后后继节点,next为node的后继节点,不能看出,后面的操作就是将这个节点移除。然后还调用unparkSuccessor()方法,唤醒后继节点。unparkSuccessor()方法,释放锁的时候再详细看。
在acquire方法中,selfInterrupt()还没看。
/**
* Convenience method to interrupt current thread.
*/
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
就是给当前线程加一个中断标志。
lock()总结
总结一下非公平锁加锁涉及到的几个方法。
ReentrantLock类:
tryAcquire->nonfairTryAcquire:尝试获得锁,有锁重入支持。
AbstractOwnableSynchronizer类:
setExclusiveOwnerThread:设置锁的持有线程。
AQS类:
acquire:获得锁
addWaiter:初始化node并入队列
acquireQueued:死循环尝试获得锁
shouldParkAfterFailedAcquire:检测获取锁失败后是否需要阻塞,实际没有阻塞操作
parkAndCheckInterrupt:阻塞当前线程,并返回线程是否中断
cancelAcquire:取消获取锁这个操作。
流程图
unLock()方法
ReentrantLock类的unLock()方法,调用的是AQS类的release()方法。
/**
* Releases in exclusive mode. Implemented by unblocking one or
* more threads if {@link #tryRelease} returns true.
* This method can be used to implement method {@link Lock#unlock}.
*
* @param arg the release argument. This value is conveyed to
* {@link #tryRelease} but is otherwise uninterpreted and
* can represent anything you like.
* @return the value returned from {@link #tryRelease}
*/
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
先调用tryRelease尝试释放。释放锁成功后,if判断head,这个判断主要是看等待队列是不是还有等待线程,没有当然就不用唤醒其他线程了。最后调用unparkSuccessor()唤醒其他等待的线程
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
// 分割线 ReentrantLock.java
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;
}
tryRelease跟tryAcquire一样,最终的实现在子类中。这里跟重入加锁的过程刚好相反,一个是state加1,一个是state-1。最后state为0就把owner线程清空同时返回true。
/**
* Wakes up node's successor, if one exists.
*
* @param node the node
*/
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
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);
}
unparkSuccessor()这个方法释放后继节点,通过CAS set当前node的状态0,找到node的next节点s。如果s为空或者取消,则从尾部向前遍历,一直遍历到头部,并不是找到就退出,是一直遍历完,找到最后一个符合条件的。这里就有个疑惑,为什么不从head遍历?
找到这个node后,就unpark唤醒它持有的线程。然后就回到加锁过程中的自旋操作。
node节点怎么移除的?在自旋操作中有一步,设置当前节点为头节点,之前的头节点就被垃圾回收了。
看完非公平锁的加解锁,可能会有一个疑问,非公平体现在哪?释放锁时候,是按照队列来唤醒的,所以还是公平的。
其实,这个非公平体现在park阻塞之前,有几次获得锁的机会。一旦进入队列并park,那么它就是公平的了。总的来说,新的线程可以抢占锁,老的线程排队等待锁,这就是非公平锁。
unLock()总结
unlock()流程比较简单,释放锁,唤醒下一节点。
tryRelease:释放锁
unparkSuccessor:唤醒后继节点
FairSync公平锁
公平锁和非公平锁,在ReentrantLock类中用的是策略模式,它们的区别,也就是看这两个类中方法的区别。对比来看。
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
// 分割线---
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.
*/
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()与tryAcquire()。
lock:相比于非公平锁,公平锁直接调用了AQS的acquire,而非公平锁,在这里给了新来的线程一次获得锁的机会。
tryAcquire:这个方法,是在AQS类的acquire()方法中调用的。在非公平锁中,直接尝试可重入地获取锁。
在公平锁中,这个尝试获取锁有给前提条件 !hasQueuedPredecessors()
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());
}
这个方法判断队列中是否有排队的node,且这个node持有的thread不是当前线程。
所以在公平锁里面,新来的线程相比于队列中的老线程,没有一次可以获得锁的机会,保证了绝对的先来后到。