精灵王's Blog


  • 首页

  • 文章归档

  • 所有分类

  • 关于我

  • 搜索
异步编程 技术方案设计 集合 设计模式 三亚 游玩 转载 Linux 观察者模式 事件 Spring SpringCloud 实战 实战,SpringCloud 源码分析 线程池 同步 锁 线程 线程模型 动态代理 字节码 类加载 垃圾收集器 垃圾回收算法 对象创建 虚拟机内存 内存结构 Java

AbstractQueuedSynchronizer(AQS) 总结篇

发表于 2020-11-23 | 分类于 同步 | 0 | 阅读次数 77

简介

在之前已经有6篇关于AQS源码分析的文章了,关于源码分析的一些问题可以去看看我之前的文章,文章连接可以在文末查看。这一篇文章主要是对AQS的一些总结,或者说是面经。

AQS是什么

AQS 全称是AbstractQueuedSynchronizer,在java.util.concurrent.locks包下面,是一个抽象的可以实现阻塞线程、排队控制、唤醒线程等操作的同步器基础框架类,AQS 可以实现排它锁、共享锁、条件锁、计数器等相关功能。

父类AbstractOwnableSynchronizer

AQS 继承的父类AbstractOwnableSynchronizer,该类仅一个属性用于记录当前持有锁的线程,提供get/set方法。

变量:同步状态state

state 字段是一个非常重要的字段,可以基于state字段的值定义出不同的同步锁功能,比如:

  1. 基于state 的值实现排他锁
    state 值为1代表锁被占用,值为0时代表锁未被占用。
    代表类:ReentrantLock
  2. 基于state的值实现读写锁
    state 被分成两部分,高16位记录读锁次数,低16位记录写锁次数
    代表类:ReentrantReadWriteLock
  3. 基于state的值实现限制线程数
    初始化一个state值,表示最大限制数,即可以做到允许最多N个线程同时运行,达到限流效果
    代表类:Semaphore
  4. 基于state的值实现倒计数
    初始化一个state值,state值为0时触发唤醒动作
    代表类:CountDownLatch

两个队列

AQS 里面有两个队列,我称为同步队列和条件队列。条件队列主要是实现条件锁时用到的队列,同步队列就是维护唤醒线程的队列。

  1. 同步队列
    主要用于维护获取互斥锁失败时入队的线程
  2. 条件队列
    调用await()的时候会释放锁,然后线程会加入到条件队列,调用signal()唤醒的时候会把条件队列中的线程节点移动到同步队列中,等待再次获得锁

可以重写的API

AQS 提供了 5 个可以自定义实现功能的API方法,基于这些方法,则可以实现不同类型的锁功能。

  1. protected boolean tryAcquire(int arg)
    尝试一次获得一个排它锁
  2. protected boolean tryRelease(int arg)
    尝试一次释放一个排它锁
  3. protected int tryAcquireShared(int arg)
    尝试一次获得一个共享锁
  4. protected boolean tryReleaseShared(int arg)
    尝试一次释放一个共享锁
  5. protected boolean isHeldExclusively()
    验证排它锁是否被占用

提供的模版方法

下面的这些模版方法,都用到了上面可以重写的API方法。

  • 基于tryAcquireAPI提供的模版方法

    1. 获得一个排它锁,直到成功获得锁

      public final void acquire(int arg) {
          if (!tryAcquire(arg) &&
              acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
              selfInterrupt();
      }
      
    2. 获得一个排它锁,可被中断

      public final void acquireInterruptibly(int arg)
                  throws InterruptedException {
          if (Thread.interrupted())
              throw new InterruptedException();
          if (!tryAcquire(arg))
              doAcquireInterruptibly(arg);
      }
      
    3. 获得一个排它锁,可超时或中断

      public final boolean tryAcquireNanos(int arg, long nanosTimeout)
                  throws InterruptedException {
          if (Thread.interrupted())
              throw new InterruptedException();
          return tryAcquire(arg) ||
              doAcquireNanos(arg, nanosTimeout);
      }
      

    其中tryAcquire(arg)方法是需要自己实现的方法

  • 基于tryAcquireShared API提供的模版方法

    1. 获得一个共享锁,直到成功获得锁

      public final void acquireShared(int arg) {
          if (tryAcquireShared(arg) < 0)
              doAcquireShared(arg);
      }
      

      其中tryAcquireShared(arg)方法是需要自己实现的方法

    2. 获得一个共享锁,可被中断

      public final void acquireSharedInterruptibly(int arg)
                  throws InterruptedException {
          if (Thread.interrupted())
              throw new InterruptedException();
          if (tryAcquireShared(arg) < 0)
              doAcquireSharedInterruptibly(arg);
      }
      
    3. 获得一个共享锁,支持超时或中断

      public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
                  throws InterruptedException {
          if (Thread.interrupted())
              throw new InterruptedException();
          return tryAcquireShared(arg) >= 0 ||
              doAcquireSharedNanos(arg, nanosTimeout);
      }
      
  • 释放一个排它锁

    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(arg)方法是需要自己实现的方法

  • 释放一个共享锁

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
    

    其中tryReleaseShared(arg)方法是需要自己实现的方法

节点状态

AQS 定义了5个队列中节点状态:

  1. 值为0,初始化状态,表示当前节点在sync队列中,等待着获取锁。
  2. CANCELLED,值为1,表示当前的线程被取消;
  3. SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
  4. CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;
  5. PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;

其他

还有一个与AQS非常相似的类——AbstractQueuedLongSynchronizer,从命名上来看,多了一个Long,从源码上来看,他们两个有完全相同的结构、属性和方法,唯一不同之处就在于所有与状态相关的参数和结果都定于为long类型,而不是int类型,当需要创建64位状态的同步器(例如多级锁和屏障)时,AbstractQueuedLongSynchronizer类可能很有用。

AQS实现源码分析

  1. 源码分析:同步基础框架之AbstractQueuedSynchronizer(AQS)
  2. 源码分析:①ReentrantLock之公平锁和非公平锁
  3. 源码分析:②ReentrantLock之条件锁Condition
  4. 源码分析:ReentrantReadWriteLock之读写锁
  5. 源码分析:Semaphore之信号量
  6. 源码分析:CountDownLatch 之倒计时门栓
  7. 源码分析:升级版的读写锁 StampedLock
精 灵 王 wechat
👆🏼欢迎扫码关注微信公众号👆🏼
👇🏼欢迎扫码关注微信公众号👇🏼
精 灵 王 微信支付

微信支付

  • 本文作者: 精 灵 王
  • 本文链接: https://jinglingwang.cn/archives/aqs
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# 异步编程 # 技术方案设计 # 集合 # 设计模式 # 三亚 # 游玩 # 转载 # Linux # 观察者模式 # 事件 # Spring # SpringCloud # 实战 # 实战,SpringCloud # 源码分析 # 线程池 # 同步 # 锁 # 线程 # 线程模型 # 动态代理 # 字节码 # 类加载 # 垃圾收集器 # 垃圾回收算法 # 对象创建 # 虚拟机内存 # 内存结构 # Java
源码分析:CountDownLatch 之倒计时门栓
①SpringCloud 实战:引入Eureka组件,完善服务治理
  • 文章目录
  • 站点概览
精 灵 王

精 灵 王

青春岁月,以此为伴

48 日志
10 分类
29 标签
RSS
E-mail
Creative Commons
Links
  • 添加友链请联系邮箱
© 2021 精 灵 王
渝ICP备2020013371号
0%