[db:标题]
摘要:Java并发探索--下篇 承接上文: 博客园【上篇】:https:www.cnblogs.comjackjavacppp18852416 csdn:【上篇】:https:blog.csdn.netokok__TXFarti
Java并发探索--下篇
承接上文:
博客园【上篇】:https://www.cnblogs.com/jackjavacpp/p/18852416
csdn:【上篇】:https://blog.csdn.net/okok__TXF/article/details/147595101
1. AQS实现锁
AQS前传
网址:https://www.cnblogs.com/jackjavacpp/p/18787832
1) aqs分析
AQS 的核心原理是通过一个 int 类型的状态变量 state 来表示同步状态,使用一个 FIFO 队列来管理等待的线程。通过 CAS 操作来保证状态的原子性更新,同时提供了独占模式和共享模式的获取与释放方法。子类可以通过重写 tryAcquire、tryRelease、tryAcquireShared、tryReleaseShared 等方法来实现具体的同步逻辑。
// 关键的属性:
// 同步状态,0 表示未锁定,大于 0 表示已锁定[>1表示可重入锁的重入次数]
private volatile int state;
// 队列的头节点
private transient volatile Node head;
// 队列的尾节点
private transient volatile Node tail;
// 其中Node的重要变量
//节点已取消: 表示该节点关联的线程已放弃等待(如超时、被中断),需从队列中移除
static final int CANCELLED = 1;
//需唤醒后继节点: 当前节点的线程释放锁或取消时,必须唤醒其后继节点。
//节点入队后需确保前驱节点的waitStatus为SIGNAL,否则需调整前驱状态。
static final int SIGNAL = -1;
//节点在条件队列中: 表示节点处于条件队列(如Condition的等待队列),而非同步队列(CLH队列)。
//状态转换:当调用Condition.signal()时,节点从条件队列转移到同步队列,状态重置为0
static final int CONDITION = -2;
//共享模式下唤醒需传播: 在共享锁(如Semaphore)释放时,确保唤醒动作传播给所有后续节点。
static final int PROPAGATE = -3;
//通过状态值控制线程的阻塞、唤醒和队列管理
volatile int waitStatus;
aqs独占锁、共享锁的获取和释放分析
独占锁
独占锁的获取:
// AbstractQueuedSynchronizer.java
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire:尝试直接获取锁,这是一个需要子类实现的方法, 尝试直接获取锁(如CAS修改state)。【非公平锁:直接尝试CAS抢占资源;公平锁:先检查队列中是否有等待线程(hasQueuedPredecessors()),避免插队】
如果第一步返回false, 则进入第二步: addWaiter:将当前线程封装成一个 Node 节点,并添加到等待队列的尾部。
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// 尝试快速插入到队列尾部
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 快速插入失败,使用 enq 方法插入
enq(node);//enq 方法会通过循环和 CAS 操作确保节点成功插入到队列尾部。
