学Java线程,你不知道什么是AQS?一文带你了解Java多线程同步的灵魂

2024-02-27 1711阅读

温馨提示:这篇文章已超过449天没有更新,请注意相关的内容是否还可用!

关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。

专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。

目录

  • 一、导读
    • 1.1 CLH锁
    • 二、概览
    • 三、使用场景
      • 3.1 AQS 对资源的共享方式
      • 四、原理
        • 4.1 原理
        • 五、 推荐阅读

          一、导读

          我们继续总结学习Java基础知识,温故知新。

          1.1 CLH锁

          CLH(Craig, Landin, and Hagersten locks)是一种自旋锁,能确保无饥饿性,提供先来先服务的公平性。

          CLH锁是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,它不断轮询前驱的状态,如果发现前驱释放了锁就结束自旋。

          学Java线程,你不知道什么是AQS?一文带你了解Java多线程同步的灵魂

          二、概览

          AbstractQueuedSynchronizer 是抽象队列同步器,是一种用来构建锁和同步器的框架。

          AQS主要做了三件事情

          • 同步状态的管理
          • 线程的阻塞和唤醒
          • 同步队列的维护

            AQS 定义了同步器的基本操作,如获取、释放和状态管理,并提供了一个等待队列来管理等待资源的线程,解决了在实现同步器时涉及的大量细节问题,例如自定义标准同步状态、FIFO 同步队列。

            基于 AQS 来构建同步器可以带来很多好处。它不仅能够极大地减少实现工作,而且也不必处理在多个位置上发生的竞争问题。

            三、使用场景

            AQS 是一个相对底层的同步器框架,对于一些常见的同步需求,Java 并发库已经提供了许多高级封装,如 ReentrantLock、ReadWriteLock、Semaphore 等,这些高级封装已经为我们提供了更简单易用的接口和功能。因此,在应用开发中,直接使用 AQS 的场景相对较少,更多的是通过使用它的子类来实现具体的同步机制。

            常用的同步器有:

            1. 独占锁(如 ReentrantLock):AQS 提供了 acquire(int arg) 和 release(int arg) 等方法,开发人员可以继承 AQS 并实现自定义的同步器来实现独占锁。通过控制同步状态(通过 getState() 和 setState(int newState) 方法),以及管理等待线程(通过等待队列),AQS 可以提供可重入锁、公平锁等不同类型的独占锁。

            2. 共享锁(如 ReadWriteLock):AQS 也可以用于实现共享锁机制,例如 ReentrantReadWriteLock。通过 acquireShared(int arg) 和 releaseShared(int arg) 等方法,开发人员可以自定义实现共享锁的逻辑。AQS 提供了对多个读线程和写线程的管理和协调,以及对读线程的优化。

            3. 实现其他同步工具:AQS 的框架还可以用于实现其他类似的同步工具,如信号量(Semaphore)、倒计时器(CountDownLatch)、循环屏障(CyclicBarrier)等。

            通过继承 AQS 并自定义同步器的行为,可以实现不同的同步机制。

            3.1 AQS 对资源的共享方式

            1. Exclusive(独占):只有一个线程能执行,如ReentrantLock。

              资源锁可分为公平锁和非公平锁:

            • 公平锁:按照线程在队列中的排队顺序(FIFO),先到者先拿到锁。

              学Java线程,你不知道什么是AQS?一文带你了解Java多线程同步的灵魂

            • 非公平锁:当线程要获取锁时,无视队列顺序直接去抢锁,谁抢到就是谁的(被唤醒的线程和新来的线程重新竞争锁)。

              学Java线程,你不知道什么是AQS?一文带你了解Java多线程同步的灵魂

              1. Share(共享):多个线程可同时执行,如Semaphore/CountDownLatch。Semaphore、CountDownLatCh、 CyclicBarrier、ReadWriteLock 我们都会在后面讲到。

              四、原理

              AQS大致流程如下:

              1、当某一线程获取锁后,将state值+1,并记录下当前持有锁的线程。

              2、再有线程来获取锁时,判断这个线程与持有锁的线程是否是同一个线程,如果是,将state值再+1,如果不是,阻塞线程(调用 LockSupport.park(this)挂起线程)。

              3、当线程释放锁时,将state值-1。

              4、当state值减为0时,表示当前线程彻底释放了锁。

              5、然后将记录当前持有锁的线程的那个字段设置为null,并唤醒其他线程,使其重新竞争锁

              4.1 原理

              AQS使用一个 Volatile的 int类型的成员 state 变量来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作(双向链表,多线程争用资源被阻塞时会进入此队列),然后通过CAS完成对State值的修改。

              其并发控制的核心是锁的获取与释放,锁的实现方式有很多种,AQS采用的是一种改进的CLH锁。

                  当state=0表示释放了锁,当state>0表示获得锁
                  /**
                   * The synchronization state.
                   */
                  private volatile int state;
                  
                  
              	封装一个Node,包含前节点,后节点,组成一个双向队列。
              	private transient volatile Node head;
              	private transient volatile Node tail;
              

              学Java线程,你不知道什么是AQS?一文带你了解Java多线程同步的灵魂

              CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。

              AQS是将每条请求共享资源的线程封装成一个CLH锁队列(FIFO同步队列)的一个结点(Node)来实现锁的分配。

              如果线程获取当前同步状态失败,AQS会将当前线程的信息封装成一个Node节点,加入同步队列中,并且阻塞该线程,当同步状态释放,则会将队列中的线程唤醒,重新尝试获取同步状态。

              我们看下node源码:

              static final class Node {
                  /** 共享节点 */
                  static final Node SHARED = new Node();
                  /** 独占节点 */
                  static final Node EXCLUSIVE = null;
                  当前节点在队列中的状态
                  volatile int waitStatus;
                  前驱指针
                  volatile Node prev;
                  后继指针
                  volatile Node next;
                  表示处于该节点的线程
                  volatile Thread thread;
                  指向下一个处于CONDITION状态的节点
                  Node nextWaiter;
              }
              

              五、 推荐阅读

              【Java基础】原子性、可见性、有序性

              【Java基础】java可见性之 Happens-before

              【Java基础】java-android面试Synchronized

              【Java基础】java-android面试-线程状态

              【Java基础】线程相关

              【Java基础】java 异常

              【Java基础】java 反射

              【Java基础】java 泛型

              【Java基础】java注解

              【Java基础】java动态代理

              【Java基础】Java SPI

              【Java基础】Java SPI 二 之 Java APT

              【Java基础】 jvm 堆、栈、方法区 & java 内存模型

              【Java基础】volatile关键字

              【Java基础】线程同步类 CountDownLatch

              【Java基础】CAS (Compare And Swap) 操作

VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]