• AQS小总结


    前言:将AQS的知识点做一个总结. 梳理了独占共享两种模式下的加解锁流程,以及两种锁的异同点. 适合日后复习

    目录

    一、AbstractQueuedSynchronizer

    1. 独占锁

    加锁

    解锁

    2. 共享锁

    加锁

    解锁

    3. 独占/共享模式对比

    加锁成功

    加锁失败

    解锁

    4. 重要属性

    waitStatus

    state

    二、ReentrantLock

    ReentrantLock类结构

    ReentrantLock源码

    三、Semaphore

    Semaphore类结构

    Semaphore源码

    四、CountDownLatch

    CountDownLatch类结构

    CountDownLatch源码

    ReentrantReadWriteLock



    一、AbstractQueuedSynchronizer

    该抽象类主要实现了:在获取锁失败时,线程如何阻塞,如何唤醒.。使用的逻辑结构为【同步等待队列】

    如何获取锁,以及获取锁是成功还是失败,由子类去定义。AQS则提供了获取锁失败后,入队/出队、阻塞/唤醒的实现。

    队列何时初始化?【尝试加锁】失败后,在addWaiter方法中,若tail为空,则新建一个Node作为头结点

    何时入队?【尝试加锁】失败后,尾插到队尾

    何时出队?当队列中有新的结点加锁成功时,原head指向的结点出队,随后head指向新的结点

    1. 独占锁

    加锁

    尝试加锁,成功则方法结束;加锁失败则将此线程尾插到"同步等待"队列

    1. // 加锁
    2. public final void acquire(int arg) {
    3. if (!tryAcquire(arg) &&
    4. acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    5. selfInterrupt();
    6. }

    protected boolean tryAcquire(int arg) :tryAcquire方法由其实现类实现:返回是否成功获取锁( 即是否成功将state由0变为1 ) 

    private Node addWaiter(Node mode) :将当前线程封装为Node, 并将其插入到队尾. 没初始化队列会先初始化

    final boolean acquireQueued(final Node node, int arg) 加锁成功则跳出方法,执行业务逻辑;或park此结点

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node)pred.ws == -1,则返回true,否则CAS设置pred.ws == -1。并把CANCELLED的结点移除队列

    解锁

    尝试解锁,解锁失败则直接返回false,成功则唤醒"同步等待"队列中第一个满足要求的结点,并返回true

    1. // 解锁
    2. public final boolean release(int arg) {
    3. if (tryRelease(arg)) {
    4. Node h = head;
    5. if (h != null && h.waitStatus != 0) // ≠0实际上就只能为-1
    6. unparkSuccessor(h); // 唤醒后继结点
    7. return true;
    8. }
    9. return false;
    10. }

    private void unparkSuccessor(Node node) :node.waitStatus<0 且 有后继结点,则unpark其后继【LockSupport.unpark(node.next.thread)】;否则取最靠前的、waitStatus<=0的结点

    2. 共享锁

    加锁

    尝试获取锁,返回负数则表示需要进行"阻塞"

    1. public final void acquireShared(int arg) {
    2. if (tryAcquireShared(arg) < 0)
    3. doAcquireShared(arg);
    4. }

     

     private static boolean shouldParkAfterFailedAcquire(Node pred, Node node)pred.ws == -1,则返回true,否则CAS设置pred.ws==-1。并把CANCELLED的结点移除队列.【同独占锁调用的相同】

    private void setHeadAndPropagate(Node node, int propagate) :设置头节点并传播唤醒,也就是说,当一个结点加共享锁成功,它会执行unparkSuccessor(Node node),进而唤醒后继结点。具体实现主要在doReleaseShared()方法👇

    1. private void setHeadAndPropagate(Node node, int propagate) {
    2. Node h = head; // Record old head for check below
    3. setHead(node);
    4. // propagate表示剩余的资源.
    5. if (propagate > 0 || h == null || h.waitStatus < 0 ||
    6. (h = head) == null || h.waitStatus < 0) {
    7. Node s = node.next;
    8. if (s == null || s.isShared())
    9. doReleaseShared();
    10. }
    11. }
    12. /*
    13. 简而言之:不断执行unparkSuccessor(h)操作,来唤醒后继结点,
    14. 退出条件为h == head,表示如果没有新的结点来代替现有头结点则退出
    15. unparkSuccessor方法同独占锁
    16. */
    17. private void doReleaseShared() {
    18. for (;;) {
    19. Node h = head;
    20. if (h != null && h != tail) {
    21. int ws = h.waitStatus;
    22. if (ws == Node.SIGNAL) {
    23. if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
    24. continue; // loop to recheck cases
    25. unparkSuccessor(h);
    26. }
    27. else if (ws == 0 &&
    28. !h.compareAndSetWaitStatus(0, Node.PROPAGATE))
    29. continue; // loop on failed CAS
    30. }
    31. if (h == head) // loop if head changed
    32. break;
    33. }
    34. }

    解锁

    尝试解锁,失败返回false;成功则执行doReleaseShared(),该方法会唤醒后继结点

    1. public final boolean releaseShared(int arg) {
    2. if (tryReleaseShared(arg)) {
    3. doReleaseShared();
    4. return true;
    5. }
    6. return false;
    7. }

    3. 独占/共享模式对比

    加锁成功

    共同点:尝试加锁,失败则addWaiter并自旋(加锁->阻塞)

    独占锁:boolean tryAcquire,返回true表示加锁成功

    共享锁:int tryAcquireShared,返回≥0代表成功

    加锁失败

    共同点:首次尝试加锁失败,调用addWaiter,并自旋(加锁->阻塞)

    在自旋尝试加锁的过程中,若加锁成功,

    独占模式下将当前结点设为头结点:head = node,失败则阻塞;

    共享模式下,不仅会将当前结点设为头结点,还会在合适条件下(如还有剩余资源),调用doReleaseShared(),来继续唤醒其后继结点。

    这也是两种模式最关键的不同

    独占模式下,只有release时才会尝试unpark头结点的后继结点;

    共享模式下,除release时会unpark外,线程在自旋时获取到锁后,也可能会unpark. 

    个人理解:doReleaseShared()方法的自旋目的在于,在队列有结点出队时,快速通知下一下结点,使其unpark.

    解锁

    共同点:唤醒后继结点

    独占模式下,head为-1则直接进行unparkSuccessor(h),随后方法结束

    共享模式下,在doReleaseShared()中调用unparkSuccessor(h). 

    代码如下,其中for循环的部分为doReleaseShared

    4. 重要属性

    waitStatus

    1. static final class Node {
    2. volatile int waitStatus
    3. }

    -1:表明后继结点需要被唤醒;结点想要被park,也需要其前驱结点为-1。在上述加解锁的方法里被置为-1的时机有:

    • shouldParkAfterFailedAcquire(Node pred, Node node)中,对于pred<=0&&≠-1,将pred.ws置为-1

    0:初始状态. 结点刚创建时的状态。在上述加解锁的方法里被置为0的时机有:

    • unparkSuccessor(Node node)中,如果ws<0, 将ws置为0【这一步我不知道有什么意义...】
    • doReleaseShared()中,调用unparkSuccessor(h)前,会将-1变为0。【有线程安全检查的作用,但不知道为何要置0】

            猜测:让即将被唤醒的线程成为头节点后,ws为0,这样后续结点可以多一次循环拿锁的机会,减小被park的概率

    state

    1. public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
    2. private volatile int state;
    3. }

    独占模式:0:空闲;1:被占有;≥1:被重入的次数

    共享模式:state:资源数,0表示没有可用资源

    二、ReentrantLock

    ReentrantLock类结构

    ReentrantLock源码

    加锁:区分公平和非公平两种模式

    1. // ReentrantLock类
    2. public void lock() {
    3. sync.acquire(1);
    4. }
    5. ------------------------------非公平锁tryAcquire------------------------------
    6. // NonfairSync类
    7. protected final boolean tryAcquire(int acquires) {
    8. return nonfairTryAcquire(acquires);
    9. }
    10. // Sync类
    11. final boolean nonfairTryAcquire(int acquires) {
    12. final Thread current = Thread.currentThread();
    13. int c = getState();
    14. if (c == 0) {
    15. if (compareAndSetState(0, acquires)) {
    16. setExclusiveOwnerThread(current);
    17. return true;
    18. }
    19. }
    20. else if (current == getExclusiveOwnerThread()) {
    21. int nextc = c + acquires;
    22. if (nextc < 0) // overflow
    23. throw new Error("Maximum lock count exceeded");
    24. setState(nextc);
    25. return true;
    26. }
    27. return false;
    28. }
    29. ------------------------------公平锁tryAcquire------------------------------
    30. // FairSync类
    31. protected final boolean tryAcquire(int acquires) {
    32. final Thread current = Thread.currentThread();
    33. int c = getState();
    34. if (c == 0) {
    35. if (!hasQueuedPredecessors() &&
    36. compareAndSetState(0, acquires)) {
    37. setExclusiveOwnerThread(current);
    38. return true;
    39. }
    40. }
    41. else if (current == getExclusiveOwnerThread()) {
    42. int nextc = c + acquires;
    43. if (nextc < 0)
    44. throw new Error("Maximum lock count exceeded");
    45. setState(nextc);
    46. return true;
    47. }
    48. return false;
    49. }

    解锁:公平与非公平模式都是调Sync的tryRelease

    1. // ReentrantLock类
    2. public void unlock() {
    3. sync.release(1);
    4. }
    5. // Sync类
    6. protected final boolean tryRelease(int releases) {
    7. int c = getState() - releases;
    8. if (Thread.currentThread() != getExclusiveOwnerThread())
    9. throw new IllegalMonitorStateException();
    10. boolean free = false;
    11. if (c == 0) {
    12. free = true;
    13. setExclusiveOwnerThread(null);
    14. }
    15. setState(c);
    16. return free;
    17. }

    三、Semaphore

    Semaphore类结构

    Semaphore源码

    加锁:区分公平和非公平两种模式

    1. // Semaphore类
    2. public void acquireUninterruptibly() {
    3. sync.acquireShared(1);
    4. }
    5. ------------------------------非公平锁tryAcquire------------------------------
    6. // NonfairSync类
    7. protected int tryAcquireShared(int acquires) {
    8. return nonfairTryAcquireShared(acquires);
    9. }
    10. // Sync类
    11. final int nonfairTryAcquireShared(int acquires) {
    12. for (;;) {
    13. int available = getState();
    14. int remaining = available - acquires;
    15. if (remaining < 0 ||
    16. compareAndSetState(available, remaining))
    17. return remaining;
    18. }
    19. }
    20. ------------------------------公平锁tryAcquire------------------------------
    21. // FairSync类
    22. protected int tryAcquireShared(int acquires) {
    23. for (;;) {
    24. if (hasQueuedPredecessors())
    25. return -1;
    26. int available = getState();
    27. int remaining = available - acquires;
    28. if (remaining < 0 ||
    29. compareAndSetState(available, remaining))
    30. return remaining;
    31. }
    32. }

     解锁:公平与非公平模式都是调Sync的tryRelease

    1. // Semaphore类
    2. public void release(int permits) {
    3. if (permits < 0) throw new IllegalArgumentException();
    4. sync.releaseShared(permits);
    5. }
    6. // Sync类
    7. protected final boolean tryReleaseShared(int releases) {
    8. for (;;) {
    9. int current = getState();
    10. int next = current + releases;
    11. if (next < current) // overflow
    12. throw new Error("Maximum permit count exceeded");
    13. if (compareAndSetState(current, next))
    14. return true;
    15. }
    16. }

    四、CountDownLatch

    countDownLatch在加锁时,只看state是否为0,无关公平与否, 因此只有公平模式

    CountDownLatch类结构

    CountDownLatch源码

    1. ---------------------------------加锁---------------------------------
    2. // CountDownLatch类
    3. public void await() throws InterruptedException {
    4. sync.acquireSharedInterruptibly(1);
    5. }
    6. // Sync类
    7. protected int tryAcquireShared(int acquires) {
    8. return (getState() == 0) ? 1 : -1;
    9. }
    10. ---------------------------------解锁---------------------------------
    11. // CountDownLatch类
    12. public void countDown() {
    13. sync.releaseShared(1);
    14. }
    15. // Sync类
    16. protected boolean tryReleaseShared(int releases) {
    17. // Decrement count; signal when transition to zero
    18. for (;;) {
    19. int c = getState();
    20. if (c == 0)
    21. return false;
    22. int nextc = c - 1;
    23. if (compareAndSetState(c, nextc))
    24. return nextc == 0;
    25. }
    26. }

    ReentrantReadWriteLock

  • 相关阅读:
    【论文阅读笔记】Pyramid Real Image Denoising Network
    IDEA远程debug教程
    Python进阶系列(十五)
    黑客或可完全控制设备,苹果紧急发布补丁!
    STM32 HAL库高级定时器输入捕获脉宽测量
    实验19:光敏传感器+继电器=光控智能灯实验
    web前端面试高频考点——Vue3.x深入理解(v-model参数用法、watch和watchEffect区别、Vue3快于Vue2、Vite启动快的原因)
    [附源码]SSM计算机毕业设计-东湖社区志愿者管理平台JAVA
    红黑树C++实现
    《微信小程序开发从入门到实战》学习十七
  • 原文地址:https://blog.csdn.net/opt1997/article/details/127590426