目录
和synchronized一样, ReentrantLock 是一个很常见常用的锁,它的底层使用了AQS ,AQS是jdk里线程同步,锁的框架。AQS比synchronized要复杂很多,AQS是一把锁,它有一个同步队列和多个等待队列。
当一个线程抢锁失败时,该线程会被封装为一个 Node,加入到同步队列中去,当线程调用await 方法时,它就进入到一个等待队列里面排队,当 signal 方法被触发时,它会从等待队列移动到锁的同步队列排队。
当然,这个Node中会有一个 waitStatus 变量来表示该线程的状态是什么,处于哪个队列。Node代码里通过CANCELLED,SIGNAL,CONDITION等类变量描述了这一点。等待队列使用Condition接口,它复用了Node对象,AQS的等待队列和synchronized的等待队列没多少区别,Condition接口的await(),signal()和Object的wait(),notify() 差不多。
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer {
static final class Node {
static final Node SHARED = new Node();
static final Node EXCLUSIVE = null;
static final int CANCELLED = 1;
static final int SIGNAL = -1;
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
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(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;
}
}
private transient volatile Node head;
private transient volatile Node tail;
private volatile int state;
}
一个ReentrantLock 的demo
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
new Thread(() -> {
try{
lock.lock();
System.out.println("模拟复杂的业务逻辑,3秒后完成");
try{
Thread.sleep(3*1000);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("线程A 已执行完毕,释放资源");
}finally {
lock.unlock();
}
},"线程A").start();
new Thread(() -> {
try{
System.out.println("线程B进行抢占");
lock.lock();
}finally {
lock.unlock();
}
System.out.println("线程B 已执行完毕,释放资源");
},"线程B").start();
}
当我们使用lock.lock()时,就会去抢锁。
ReentrantLock有公平锁和非公平锁的实现,默认是非公平锁。
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
非公平锁这样加锁,它想把AbstractQueuedSynchronizer的state 变量通过CAS的方式从0修改为1。
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
如果修改成功,就会获取锁,失败就走acquire(1)分支,ReentrantLock没有覆写acquire(),而是继续沿用了AQS的方法,所以AQS就是锁的框架 。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire(),addWaiter(),acquireQueued()都是模板方法,父类定义了流程顺序,子类根据需要可以覆写。
在ReentrantLock的非公平实现 NonfairSync 中,再次通过CAS,可重入的方式试探还能否加锁。
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;
}
加锁
失败后进入addWaiter(Node.EXCLUSIVE),其中static final Node EXCLUSIVE = null。另外AbstractQueuedSynchronizer的tail,pred两个节点初始时肯定都为null。 主要,头结点head,尾结点tail是AQS的变量,而前一个节点pred,后一个节点next是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
// tail 是 AQS的tail变量
Node pred = tail;
if (pred != null) {
node.prev = pred;
// compareAndSetTail(期望值,更新值),后续也如此
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
自旋加入,for循环+CAS,就是无锁自旋。如果尾结点为空,进入enq(node),会加入一个空节点作为头结点,第一个加入的Node为尾结点,这个哨兵节点可以理解为是初始化的一部分。变成这样:
所以第一次for循环会新建哨兵节点,第二次进入for循环才会把节点加入进链表。在for循环里,拷贝一份tail变量命名为t,然后后续都使用t进行操作,最后更新tail (tail = head;),有点类似于写时复制。
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;
}
}
}
}
此时线程B抢锁失败,进入到队列。
AQS#acquireQueued中,又是for循环包裹的代码,自旋,每个还未被阻塞的 Node 节点都监听自己的前序节点,如果前序节点是同步队列的 head 节点,就尝试抢锁。
// arg 为 1
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;
}
// 判断是否要阻塞掉抢锁失败的当前线程,不然又进入到 for 循环自旋
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
node.predecessor()会获取到node节点的前一个节点,如果前一个节点pred就是AQS的头结点head,则该Node对应的线程会再次抢锁,抢锁成功就会把AQS的头结点head设置为node。
抢锁失败进入shouldParkAfterFailedAcquire()方法,判断是否需要阻塞掉该线程。
// pred是头结点,node是线程B
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// waitStatus没找到赋值的地方,默认为0,可以debug验证
int ws = pred.waitStatus;
if (ws == Node.SIGNAL) //Node.SIGNAL=-1
// 第二次for循环走这里,才会开始阻塞线程B
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
// 第一次for循环走这,设置头节点的waitStatus为-1
// 第一次for循环算是环境初始化的一部分吧,比较绕
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
static final int CANCELLED = 1;
static final int SIGNAL = -1;
static final int CONDITION = -2;
static final int PROPAGATE = -3;
parkAndCheckInterrupt() ,
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
当线程A释放锁时,lock.unlock()
// arg 为 1
public final boolean release(int arg) {
if (tryRelease(arg)) {
// 获取AQS的头结点
Node h = head;
// 头结点不为空且头结点的waitStatus不为0,说明有线程被阻塞了
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
锁释放
ReentrantLock#tryRelease() ,对 state进行减1操作,如果本线程不是持有锁的线程,直接报错。
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;
}
unparkSuccessor 会先把头结点head的waitStatus设置为0,
//node是头结点head
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
// 默认同步队列节点的状态是 0
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next; // s 是线程B,不为 null
// s.waitStatus > 0 的情况只有 CANCELLED 状态
if (s == null || s.waitStatus > 0) { // waitStatus==0
s = null;
// 从尾结点开始往前遍历,
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// 唤醒 s 线程去抢锁,让 s 线程继续在 acquireQueued 方法里的 for 循环执行
if (s != null)
LockSupport.unpark(s.thread);
}
线程B被唤醒了。线程B会唤醒后会做什么呢?抢锁,其实线程B一直处于acquireQueued()方法的for循环中的,只不过它有段时间被迫阻塞了而已,被唤醒后还会一直抢锁。
// arg 为 1
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;
}
// 抢锁线程已进入同步队列,park 阻塞掉
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
Condition 等待队列
前面主要使用了AQS的同步队列,还有等待队列。不过等待队列一般不常用。
下面是一个使用Condition的demo。
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(() -> {
try{
lock.lock();
System.out.println("线程A 抢到锁,模拟复杂的业务逻辑,3秒后完成");
try{
Thread.sleep(3*1000);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("线程A 已执行中止,进入等待队列");
try {
condition.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("线程A 从等待队列出来,继续执行,执行完毕");
}finally {
lock.unlock();
}
},"线程A").start();
new Thread(() -> {
try{
System.out.println("线程B 进行抢占");
lock.lock();
condition.signal();
}finally {
lock.unlock();
}
System.out.println("线程B 已执行完毕,释放资源");
},"线程B").start();
}
lock.newCondition() 返回一个 ConditionObject 。
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
private transient Node firstWaiter;
private transient Node lastWaiter;
private static final int REINTERRUPT = 1;
private static final int THROW_IE = -1;
}
condition.await()
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
// 当前持有锁的线程已经加入到等待队列中后,就要释放锁了
int savedState = fullyRelease(node);
int interruptMode = 0;
// 判断节点是否在同步队列里,如果执行了 signal() 会导致Node线程转移到同步队列,退出循环
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
// 中断可以退出循环
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// acquireQueued() 开始抢锁,只有抢锁成功它才会返回true,如果之前有过中断,则再次中断。
if (acquireQueued(node, savedState) && interruptMode != THROW_IE) // THROW_IE=-1
interruptMode = REINTERRUPT; // REINTERRUPT=1
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0) // 处理中断
reportInterruptAfterWait(interruptMode);
}
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;
}
private void reportInterruptAfterWait(int interruptMode)
throws InterruptedException {
if (interruptMode == THROW_IE)
throw new InterruptedException();
else if (interruptMode == REINTERRUPT)
selfInterrupt();
}
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
unlinkCancelledWaiters()消除所有状态不是CONDITION的节点,把trail 变量理解为prev 就懂了。
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;
}
}
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;
}
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;
}
checkInterruptWhileWaiting() 会在Node进入等待队列时检查是否有中断产生。
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
// 线程被唤醒可能是因为被中断,也可能是因为其他线程执行了signal(),在执行signal()时,会设置Node等待状态值为0
final boolean transferAfterCancelledWait(Node node) {
// 尝试把Node节点状态从 CONDITION 修改为0,修改成功就加入同步队列。如果原先Node的状态为CONDITION,说明不是signal()操作,而是中断唤醒了。
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
enq(node);
return true;
}
// 已经执行了signal()操作,直接让出cpu即可。
while (!isOnSyncQueue(node))
// yield() 会让当前线程让出cpu
Thread.yield();
return false;
}
signal()方法会把等待队列的第一个节点加入到同步队列中去。
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
// If cannot change waitStatus, the node has been cancelled.
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
// 把节点加入到同步队列
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
总结: state是锁的控制变量,抢锁失败就进入一个同步队列,这个同步队列的头节点就一个哨兵节点,起到监控整个队列的左右,当线程抢锁失败,会自旋两三次后进入这个同步队列并被阻塞,当锁释放时,会唤醒哨兵节点之后的第一个非空节点去抢锁。 AQS作为锁的框架,ReentrantLock只使用到了AQS的一部分,像其他的锁,其他的同步类则使用了AQS的其他部分。
未完待续。。。
本文详细解析了ReentrantLock的内部实现,包括其如何基于AQS(AbstractQueuedSynchronizer)管理锁状态,以及线程在获取锁失败后如何进入同步队列等待,同时介绍了Condition的使用,展示了await()和signal()方法的工作原理。
444

被折叠的 条评论
为什么被折叠?



