在前文已经讲述ReentrantLock如何使用,本章将对源码进行分析。
在ReentrantLock的构造函数中,我们看到,默认采用的是非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
在类图中,我们看到了如雷贯耳的AQS,下面先分析AQS
1.1. AQS是啥
根据源码doc,AQS是一个依赖FIFO,用于实现堵塞锁和同步器的框架,且其子类最好定义为内部类
其核心就是双端同步队列
1.2 双端核心队列
队列有Node节点构成,其是一个内部静态类,
* +------+ prev +-----+ +-----+
* head | | <---- | | <---- | | tail
* +------+ +-----+ +-----+
源码如下:
static final class 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;
/**
* Status field, taking on only the values:
* SIGNAL: The successor of this node is (or will soon be)
* blocked (via park), so the current node must
* unpark its successor when it releases or
* cancels. To avoid races, acquire methods must
* first indicate they need a signal,
* then retry the atomic acquire, and then,
* on failure, block.
* CANCELLED: This node is cancelled due to timeout or interrupt.
* Nodes never leave this state. In particular,
* a thread with cancelled node never again blocks.
* CONDITION: This node is currently on a condition queue.
* It will not be used as a sync queue node
* until transferred, at which time the status
* will be set to 0. (Use of this value here has
* nothing to do with the other uses of the
* field, but simplifies mechanics.)
* PROPAGATE: A releaseShared should be propagated to other
* nodes. This is set (for head node only) in
* doReleaseShared to ensure propagation
* continues, even if other operations have
* since intervened.
* 0: None of the above
*
* The values are arranged numerically to simplify use.
* Non-negative values mean that a node doesn't need to
* signal. So, most code doesn't need to check for particular
* values, just for sign.
*
* The field is initialized to 0 for normal sync nodes, and
* CONDITION for condition nodes. It is modified using CAS
* (or when possible, unconditional volatile writes).
*/
volatile int waitStatus;
/**
* Link to predecessor node that current node/thread relies on
* for checking waitStatus. Assigned during enqueuing, and nulled
* out (for sake of GC) only upon dequeuing. Also, upon
* cancellation of a predecessor, we short-circuit while
* finding a non-cancelled one, which will always exist
* because the head node is never cancelled: A node becomes
* head only as a result of successful acquire. A
* cancelled thread never succeeds in acquiring, and a thread only
* cancels itself, not any other node.
*/
//前驱节点
volatile Node prev;
/**
* Link to the successor node that the current node/thread
* unparks upon release. Assigned during enqueuing, adjusted
* when bypassing cancelled predecessors, and nulled out (for
* sake of GC) when dequeued. The enq operation does not
* assign next field of a predecessor until after attachment,
* so seeing a null next field does not necessarily mean that
* node is at end of queue. However, if a next field appears
* to be null, we can scan prev's from the tail to
* double-check. The next field of cancelled nodes is set to
* point to the node itself instead of null, to make life
* easier for isOnSyncQueue.
*/
//后继节点
volatile Node next;
/**
* The thread that enqueued this node. Initialized on
* construction and nulled out after use.
*/
volatile Thread thread;
/**
* 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
Node nextWaiter;
/**
* Returns true if node is waiting in shared mode.
*/
final boolean isShared() {
return nextWaiter == SHARED;
}
/**
* 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() { // Used to establish initial head or SHARED marker
}
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
Node节点的状态有如下几种:
- 默认状态为 0;
- waitStatus > 0 (CANCELLED 1) 说明该节点超时或者中断了,需要从队列中移除;
- waitStatus = -1 SIGNAL 当前线程的前一个节点的状态为 SIGNAL,则当前线程需要阻塞(unpark);
- waitStatus = -2 CONDITION -2 :该节点目前在条件队列,如果不使用condition用不到;
- waitStatus = -3 PROPAGATE -3 :releaseShared 应该被传播到其他节点,在共享锁模式下使用。
AQS有两个队列一个是同步队列,一个是条件队列,其节点共用Node,发现一张比较好的图,分别如下
同步队列
条件队列
1.3 lock源码分析
以公平锁为例,lock整体流如下:
acquire()方法
final void lock() {
if (compareAndSetState(0, 1))
1. setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
public final void acquire(int arg) {
//方法后面会介绍
2. if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire分析如下:
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()) {
//此处就是可重入,将状态值增加acquires,一般是加1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//返回false,说明要去队列排队
return false;
}
addWaiter分析如下: 将当前线程与node绑定,并加入到同步队列
private Node addWaiter(Node mode) {
//线程和node绑定
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;
//采用反射,将node节点设置为尾节点
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
enq()方法如下:
3. private Node enq(final Node node) {
//自旋
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
//使用CAS,初始化头尾节点
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
//第一个是期望值,第二值是更新后的值,方法利用反射设置tail的值
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
假定已经初始化head节点,那么head和tail指向是同一个对象,红色节点是已队列,蓝色是新增的,上面的流程步骤如下,其中header是虚拟节点:
acquireQueued分析如下:
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
//进行自旋
for (;;) {
//这不分方法很重要,和后面的释放有着密切关系
//在自选过程中,会一直前检查前面一个,若其是前面一个节点,那么在没有
//外来线程继续抢占锁锁的情况,下一个获得到锁的线程就这这个锁
final Node p = node.predecessor();
//因为head是虚拟节点,所以此处线程可以直接去获取锁
4. if (p == head && tryAcquire(arg)) {
setHead(node);
//在释放锁的过程中,我们没有发现将head节点移除的过程,其实这里
//将next=null,就是将head节点给回收掉
p.next = null; // help GC
failed = false;
return interrupted;
}
//parkAndCheckInterrupt方会执行LockSupport.park方法,将当前线程挂起,等待unprk唤醒
//当然,只有shouldParkAfterFailedAcquire执行成功才会执行后面的方法
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire分析如下:
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.
*/
//将前置节点的状态置为SIGNAL,注意这样的话head节点也是SIGNAL状态,在释放锁的时候
//会用到
5. compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
根据上面可知,只有前驱节点的状态为SIGNAL,才能将当前线程机型park(线程未waiting状态)
parkAndCheckInterrupt分析如下:
private final boolean parkAndCheckInterrupt() {
//使线程置为waiting状态,并挂起
6. LockSupport.park(this);
return Thread.interrupted();
}
上面打断挂起线程,有两种方式,1:LockSupport.unpark(thread); 2:thread.interrupt() 如果是LockSupport.unpark打断,那么Thread.interrupted()为false; 如果是thread.interrupt()打断,Thread.interrupted()返回为true,同时将中断标志位 置为false,这个和1.2.1 acquire()对应,因为此处将中断位置为false了,那么1.2.1就要将自中断,将中断标志位置为true
#### 1.3 unlock源码分析
public final boolean release(int arg) {
if (tryRelease(arg)) {
//当前节点释放后,要把head的下一个节点唤醒
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
tryRelease方法
protected final boolean tryRelease(int releases) {
//没有使用cas,因为释放的时候,肯定是持有lock的
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;
}
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.
*/
//从后向前找到下一个状态不是canceled的节点
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);
}
1.4 unlock源码分析
public final boolean release(int arg) {
//将exclusiveOwnerThread设置为null
if (tryRelease(arg)) {
//经过上一步锁已经释放了,下面只是将同步队列的head节点后面需要唤醒的节点唤醒
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
unparkSuccessor方法:
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)
//会唤醒park的thread
LockSupport.unpark(s.thread);
}
释放锁的实际分为两步,第一步释放锁,第二步将节点置为初始状态并park线程,锁释放后,可能是同步队列中的线程获取到,也有可能是新来的线程获取到
总结
单独看加锁和释放所得代码,比较难串到一起,代码确实很巧妙写的,利用cas和Locksupport.park和unpark实现了锁
不考虑重入,以公平锁为例,多个线程在不停抢锁 加锁流程:
上面个点的标注在代码中有体现,要特别注意的是header存在且header状态为signal说明有线程已经获取到锁了,其他线程只能等待
exclusiveOwnerThread =thread 释放锁流程: