zoukankan      html  css  js  c++  java
  • AbstractQueuedSynchronizer(AQS) 总结篇

    简介

    在之前已经有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 之倒计时门栓
  • 相关阅读:
    【UOJ #268】【清华集训2016】数据交互(动态DP)
    【UOJ #267】【清华集训2016】魔法小程序(前缀和)
    【UOJ #266】【清华集训2016】Alice和Bob又在玩游戏(SG函数+01Trie)
    【CSP-S 2019题解】
    【CSP 2019游记】
    【CSP-S 2019模拟题解】
    sql语句: update和sql函数的冲突
    http协议之实践巩固(深度篇一)
    不错的开发工具做下记录
    javascrpt之this指向问题
  • 原文地址:https://www.cnblogs.com/admol/p/14023559.html
Copyright © 2011-2022 走看看