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 之倒计时门栓
  • 相关阅读:
    volley框架使用
    Insert Interval
    candy(贪心)
    Best Time to Buy and Sell Stock
    Best Time to Buy and Sell Stock III
    distinct subsequences
    edit distance(编辑距离,两个字符串之间相似性的问题)
    trapping rain water
    word break II(单词切分)
    sudoku solver(数独)
  • 原文地址:https://www.cnblogs.com/admol/p/14023559.html
Copyright © 2011-2022 走看看