大家可以看一下这个继承关系图:
ReentrantLock 有两种锁:公平锁和非公平锁。
1.非公平锁原理
先从构造器来看,ReentrantLock有默认实现是非公平锁。
而NoFairSync继承自Sync
AQS有一些常见字段:
- state: 同步状态
- head:等待队列的头部,延迟初始化。除初始化外,仅通过 setHead 方法进行修改。注意:如果 head 存在,则保证其 waitStatus 不会被 CANCELLED
- tail 等待队列的尾部,延迟初始化。仅通过方法 enq 进行修改以添加新的等待节点
- exclusiveOwnerThread 当前获得锁的线程
加锁
没有竞争的时候,直接将exclusiveOwnerThread设置为当前线程,state =1
当Thread 1 来竞争的时候:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
Thread-1 尝试CAS 0-> 失败,结果失败。
进入tryAcquire逻辑,这时候state已经是1,结果仍然失败。
接下来addWaiter逻辑,将当前的节点添加到head的末尾,返回新节点
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) {
//将新建的节点前驱指向tail
node.prev = pred;
//将tail的指针指向最后一个节点
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//尾节点为null情况 将节点添加到队列末尾
enq(node);
return node;
}
如下图所示:
图中黄色三角表示该 Node 的 waitStatus(与state不同) 状态,其中 0 为默认正常状态
Node 的创建是懒惰的。
其中第一个 Node 称为 Dummy(哑元)或哨兵,用来占位,并不关联线程
当前线程进入 acquireQueued 逻辑
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//获得此节点的前驱节点
final Node p = node.predecessor();
//如果它为head,也就是node是第二个节点的情况下并且此tryAcquire获取锁
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);
}
}
1. acquireQueued 会在一个死循环中不断尝试获得锁,失败后进入 park 阻塞
2. 如果自己是紧邻着 head(排第二位),那么再次 tryAcquire 尝试获取锁,当然这时 state 仍为 1,失败
3. 进入 shouldParkAfterFailedAcquire 逻辑,将前驱 node,即 head 的 waitStatus 改为 -1(表示他有责任唤醒他的后继节点),这次返回 false
4.shouldParkAfterFailedAcquire 执行完毕回到 acquireQueued ,再次 tryAcquire(第三次) 尝试获取锁,当然这时 state 仍为 1,失败
5.当再次进入 shouldParkAfterFailedAcquire 时,这时因为其前驱 node 的 waitStatus 已经是 -1,这次返回 true
6. 进入 parkAndCheckInterrupt, Thread-1 park(灰色表示)
再次有多个线程经历上述过程竞争失败,变成这个样子
hread-0 释放锁,
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 流程,如果成功
设置 exclusiveOwnerThread 为 null
state = 0
当前队列不为 null,并且 head 的 waitStatus = -1,进入 unparkSuccessor 流程
找到队列中离 head 最近的一个 Node(没取消的),unpark 恢复其运行,本例中即为 Thread-1
回到 Thread-1 的 acquireQueued 流程,复活了
如果加锁成功(没有竞争),会设置
exclusiveOwnerThread 为 Thread-1,state = 1
head 指向刚刚 Thread-1 所在的 Node,
该 Node 清空 Thread 原本的 head 因为从链表断开,而可被垃圾回收
如果这时候有其它线程来竞争(非公平的体现),例如这时有 Thread-4 来了
如果不巧又被 Thread-4 占了先
Thread-4 被设置为 exclusiveOwnerThread,state = 1
Thread-1 再次进入 acquireQueued 流程,获取锁失败,重新进入 park 阻塞
加锁源码
//先进行acquire
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
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);
}
}
/**
检查并更新获取失败的节点的状态。如果线程应阻塞,则返回 true。这是所有采集环路中的主要信号控制。要求 pred == node.prev。
参数:
pred – 节点的前身持有状态 node – 节点
返回:
true 如果线程应该阻塞
*/
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;
}
/**
park的方法,然后检查是否中断
返回:
true 如果中断
*/
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
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;
}
}
}
}
注意:
是否需要 unpark 是由当前节点的前驱节点的 waitStatus == Node.SIGNAL 来决定,而不是本节点的 waitStatus 决定
解锁源码
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}