AQS
java.util.concurrent.locks.AbstractQueuedSynchronizer,译为形象队列式同步器
AQS 提供了原子式治理同步状态、阻塞和唤醒线程性能以及期待队列模型的简略框架;
AQS 蕴含了一个虚构的Node双向链表(即期待队列),由 volatile 润饰的头&尾节点,以及同步状态标记state,节点期待状态标记waitStatus,和以后线程信息
AQS 提供了共享锁与独占锁的反对
独占锁
同一个时刻只能被一个线程占有,如ReentrantLock,ReentrantWriteLock等,其中又蕴含了
偏心锁与非偏心锁
共享锁
同一时间点能够被多个线程同时占有,如ReentrantReadLock,Semaphore等
偏心锁 & 非偏心锁
初始化
// 偏心锁 ReentrantLock lock = new ReentrantLock(true); // 非偏心锁 ReentrantLock lock = new ReentrantLock(); ReentrantLock lock = new ReentrantLock(false);
加锁过程
java.util.concurrent.locks.ReentrantLock.FairSync#lock
final void lock() { // 尝试将以后锁的状态置为1 即为加锁胜利 acquire(1); } public final void acquire(int arg) { /** * 尝试加锁 tryAcquire = true 则加锁胜利 * 加锁失败时进入后续的期待流程 acquireQueued * acquireQueued 将有限循环获取锁 * 若获取到锁间接开始执行 * 或前节点成为头部 本人行将执行 则临时挂起 */ if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
1.尝试加锁(偏心锁模式)
/** * 尝试获取锁 设置锁状态 锁状态的数值理论为获取锁线程的加锁的次数(可重入) * 返回true为加锁胜利 false为失败 */ protected final boolean tryAcquire(int acquires) { // 以后线程 final Thread current = Thread.currentThread(); // 获取以后锁的状态 int c = getState(); // 以后锁状态为0 则为能够加锁 if (c == 0) { /** * 非偏心锁模式将不会判断队列是否有其余节点 会间接尝试获取锁 * hasQueuedPredecessors = false * 则期待队列中没有其余线程 * 即以后线程是下一个该当获取锁的线程 */ if (!hasQueuedPredecessors() && /** * 思考到有其余线程同时判断结束 * 故进行CAS操作 尝试批改锁状态 */ compareAndSetState(0, acquires)) { // 前两步都已胜利 则将以后线程设置为持有锁的线程 setExclusiveOwnerThread(current); return true; } } // 以后锁状态不为0 且持有锁的线程是以后线程 则间接重入 else if (current == getExclusiveOwnerThread()) { // 设置以后锁状态+1 即以后线程总共上锁的次数 int nextc = c + acquires; // 若上锁次数为负 实为上锁次数越界 if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
判断是否期待队列中是否有其余线程(非偏心锁没有此步骤)
public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized // before tail and on head.next being accurate if the current // thread is first in queue. Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; /** * 队列头部 与 队列尾部元素 不相等 * 且头部后一个元素不为空 或头部后一个期待线程不是以后线程 * 阐明队列中至多有其余的线程在期待 */ return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());}
CAS操作进行锁状态变更
protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update);}
2.加锁失败 进入期待队列
/** * 将以后线程构建为期待队列的元素 即Node * 下文将用Node示意以后线程构筑的期待队列元素 * 将该Node期待的线程设置为以后锁的持有线程 * CAS操作 尝试将以后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 Node pred = tail; // 判断以后尾部元素 是否为空 if (pred != null) { // 尾部不为空 设置尾部元素为以后线程Node 的前节点元素 node.prev = pred; // 尝试将以后线程Node设置成尾部元素 if (compareAndSetTail(pred, node)) { // 胜利后 批改旧的队尾元素的后一个节点为以后Node pred.next = node; return node; } } enq(node); return node;} final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; // 以后Node会在此处有限循环 直到前节点成为头部 本人获取到锁才返回 for (;;) { // 获取以后Node的前节点 final Node p = node.predecessor(); // 若以后节点的前节点已成为队列头部 则再次尝试加锁 if (p == head && tryAcquire(arg)) { // 胜利后 设置以后Node为头部元素 setHead(node); p.next = null; // help GC // 此时已获取到锁 故无需再设置本人为中断状态 // 进入队列后果则为失败 failed = false; return interrupted; } /** * shouldParkAfterFailedAcquire 判断以后线程是否可能挂起 * 革除队列中已勾销的节点 * 判断为true 则能够挂起 */ if (shouldParkAfterFailedAcquire(p, node) && /** * parkAndCheckInterrupt 执行挂起 * 并返回以后线程是否有过中断请求 */ parkAndCheckInterrupt()) /** * 以后线程在期待过程中无奈响应中断 直到获取到锁 * 如果在整个期待过程中被中断过 则interrupted = true * acquireQueued最终返回true 否则返回false * 并在外层响应中断 * 须要其余线程调用以后线程的interrupt()办法 */ interrupted = true; } } finally { if (failed) cancelAcquire(node); }}
参考资料:
https://blog.csdn.net/Java_zh...
https://blog.csdn.net/Leon_Ji...
https://blog.csdn.net/yy_dieg...