一、总体思路
二、ReentrantLock#lock方法获取锁为入口
三、release释放锁
(以open-jdk 1.8.0为源码分析版本)
lock获锁
一、总体思路
AQS未使用原生Synchronized机制支持,在获锁的过程必须自我实现获锁、释放锁、线程阻塞、线程唤醒等功能。利用CLH虚拟的双向队列结构,
在未获锁情况下线程封装为队列节点入列阻塞等待,释放锁时候唤醒等待节点
- CLH (Craig, Landin, and Hagersten)是一个虚拟的双向队列结构,AQS中只是保留了头部(head)和尾部(tail)
CLH头部和尾部节点
/**
* 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;
-
CLH虚拟队列结构如下图:带头(dummy head)双向链表节点
-
volatile修饰的整形变量state标识锁的状态:state可大于1,以此来实现锁可重入(即获锁的线程允许再次获锁)
/**
* 同步锁状态
*/
private volatile int state;
protected final int getState() {
return state;
}
protected final void setState(int newState) {
state = newState;
}
/**
* cas原子性更新state
*/
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
源码分析
(1)、以ReentrantLock#lock方法获取锁为入口
public void lock() {
sync.lock();
}
(2)、sync为内部变量,构造ReentrantLock时根据参数创建公平锁和非公平锁,空参构造默认创建非公平锁。
/** Synchronizer providing all implementation mechanics */
private final Sync sync;
/**
* 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();
}
(3)、Sync抽象类继承自AbstractQueuedSynchronizer(AQS),NonFairSync和FairSync都继承自Sync实现非公平锁和公平锁机制。
/**
* Base of synchronization control for this lock. Subclassed
* into fair and nonfair versions below. Uses AQS state to
* represent the number of holds on the lock.
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
final void lock() {
acquire(1);
}
// ......
}
/**
* Sync object for non-fair locks
*/
static final class NonfairSync extends Sync {
//......
}
/**
* Sync object for fair locks
*/
static final class FairSync extends Sync {
// ......
}
(4)、先以FairSync为例,FairSync最终将调用AbstractQueuedSynchronizer#acquire(int arg)获锁
(其中arg参数即为获取锁的数量,要完全释放锁则获取多少数量,释放锁时必须释放对应数量)
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
依次分析核心方法:
- tryAcquire(int arg):尝试获锁
- addWaiter(Node node):节点进入CLH等待队列
- acquireQueued(Node node, int arg):节点是否可以获锁,获取不到即阻塞等待
- selfInterrupt():自我产生中断
(4.1)、tryAcquire实现在FairSync内部。
/**
* Sync object for fair locks
*/
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();
// 获取锁状态 0表示锁无人占用
int c = getState();
if (c == 0) {
// 此时无前继节点即代表当前节点在队列头部,则利用cas原子获锁
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
// 利用AbstractOwnableSynchronizer提供的基础支持,设置当前线程为锁拥有者
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;
}
}
(4.2)、再来看看实现 AbstractQueuedSynchronizer#addWaiter(Node node) 节点入列的过程:
- CLH双向虚拟队列的节点Node类
static final class Node {
/** 标识为分享模式 */
static final Node SHARED = new Node();
/** 标识为独占模式 */
static final Node EXCLUSIVE = null;
/** 节点状态 0不表示属于以下任务状态 */
/** 表示当前节点已取消等待锁 */
static final int CANCELLED = 1;
/** 表示当前节点需要唤醒状态,同时"后继节点"需要被阻塞 */
static final int SIGNAL = -1;
/** 表示当前节点在等待Condition唤醒 */
static final int CONDITION = -2;
/** 表示其它线程获取到“共享锁”,对应的waitStatus的值 */
static final int PROPAGATE = -3;
volatile int waitStatus;
/**
* 前继节点
*/
volatile Node prev;
/**
* 后继节点
*/
volatile Node next;
/**
* 节点所对应的线程
*/
volatile Thread thread;
/**
* nextWaiter是“区别当前CLH队列是 ‘独占锁’队列 还是 ‘共享锁’队列 的标记”
* 若nextWaiter=SHARED,则CLH队列是“共享锁”队列;
* 若nextWaiter=EXCLUSIVE,(即nextWaiter=null),则CLH队列是“独占锁”队列。
*/
Node nextWaiter;
final boolean isShared() {
return nextWaiter == SHARED;
}
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;
}
}
- 入列逻辑:如队列不存在则会初始化,否则直接加入队尾。
/**
* 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) {
// 根据mode判断队列模式的标志,
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
// 如果CLH队列非空,将节点插入队尾
if (pred != null) {
node.prev = pred;
// cas实现原子更新
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// CLH队列为空将初始化队列
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize 未初始化则新增dummy head 且 head == tail
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
假设CLH队列为空,插入node节点过程:
/**
* 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 (;;) {
// 当前节点已入列, 根据公平锁的原则判断当前节点前继p是否与head相等。
// 队列为带头双向链表:公平原则核心体现在”p == head“即代表node为第一个节点,具有获锁的权利,否则即使被唤醒也无权利获锁。
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 获锁失败,判断当前节点是否需要被阻塞等待
// 1、阻塞,即将会等待其他线程调用LockSupport#unpark 或者 收到线程中断 唤醒
// 2、非阻塞,即再进入for死循环竞争锁
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// "前继节点"状态为Node.SIGNAL返回true 即表示当前节点将会被阻塞等待,等待"前继节点"释放锁,调用LockSupport#unpark唤醒
// 其余返回false
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
// 状态大于0即表示取消状态
if (ws > 0) {
/*
* 从pred开始往前遍历,清除掉ws>0 即已经取消状态的节点
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* 如果前继节点为“0”或者“共享锁”状态,则设置前继节点为SIGNAL状态。
* 此时需要调用者再次进入尝试,确认节点是否需要阻塞
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
private final boolean parkAndCheckInterrupt() {
// 利用LockSupport辅助类将线程阻塞
LockSupport.park(this);
// 判断状态:是否被因产生中断被唤醒,还是被LockSupport#unpark唤醒
return Thread.interrupted();
}
补充:被LockSupport#park阻塞的线程在几种情况下会被唤醒返回,因何种情况唤醒不得而知需要自我识别:
- 调用LockSupport#unpark唤醒
- 调用Thread#interrupt唤醒
- 调用者无理由返回唤醒
(4.4)、先看看AbstractQueuedSynchronizer#selfInterrupt()的代码实现
static void selfInterrupt() {
// 当前线程自我产生中断标记
Thread.currentThread().interrupt();
}
为什么会在acquireQueued返回的时候还要自我产生中断呢?
- 首先,由于parkAndCheckInterrupt方法为了识别线程由于何种原因导致线程唤醒(一般为前两种情况的判断),会调用Thread.interrupted()方法,清除中断标记并返回。
- 其次,Thread.interrupted()清除了中断标记,将会导致后续线程操作无法识别到中断标记,所以在确定了被线程中断唤醒情况下,自己重新生成一个中断。
- 非公平锁NonFairSync和公平锁FairSync不同之处:如果锁未被占用则立即获取锁,不管节点是否为CLH队列头部。
/**
* Sync object for non-fair locks
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* 1、如锁未被占用,立即获取锁
* 2、获锁失败再次进入 AbstractQueuedSynchronizer#acquire方法
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
// 实现在Sync内部
return nonfairTryAcquire(acquires);
}
}
由于在分析公平锁FairSync已经分析过acquire方法,这里不再赘述。
- Sync#nonfairTryAcquire非公平锁实现:
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
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();
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;
}
// ********
}
release释放锁
(1)、以ReentrantLock#unlock方法释放锁为入口。注意:独占锁释放线程必须为锁持有者,否则将抛出IllegalMonitorStateException异常
public void unlock() {
sync.release(1);
}
(2)、sync为内部变量,实际将调用AbstractQueuedSynchronizer#release释放锁
public final boolean release(int arg) {
// 尝试释放锁
if (tryRelease(arg)) {
// 获取锁的节点必定为CLH头节点
Node h = head;
if (h != null && h.waitStatus != 0)
// 唤醒其后继节点
unparkSuccessor(h);
return true;
}
return false;
}
将分为两个步骤:
- tryRelease: 尝试释放锁
- unparkSuccessor:唤醒CLH队列最靠近头节点的有效后继节点
(2.1)、Sync类方法tryRelease逻辑很明确,即 设置state状态、清除锁持有者线程为null
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;
}
(2.2)、AbstractQueuedSynchronizer#unparkSuccessor方法唤醒继节点,即最靠近头节点的有效后继节点
private void unparkSuccessor(Node node) {
/*
* 如果当前节点状态为负数(意味着可能会被再次唤醒),因而设置该状态为0
* 节点状态修改可能失败或者被其他等待线程修改,但不影响逻辑。
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* 节点释放锁则唤醒队列的下一个节点, 即:
* 1、下一个节点不为null 且 状态为非取消 即唤醒。
* 2、上述对Node节点分析, 下一个节点waitStatus>0表示为取消状态,此时从末尾遍历至node节点,
* 找到最后一个非取消状态节点并唤醒,否则不做任何操作。
*/
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);
}
公平锁 和 非公平锁释放锁逻辑一致,只需分析一个即可。