Java当中的AQS

一、什么是AQS 

AQS的全称是:AbstractQueuedSynchronizer

AQS是java当中的一个抽象类,用来构建锁同步器

例如我们常见的ReentrantLock,Semaphore等等都是通过AQS来构建的。


AQS的原理

       如果被请求的共享资源没有被占用,那么就把请求资源的线程设置为有效的线程,允许申请,并且把共享资源的线程设置为锁定状态


       如果被请求的共享资源已经被其他的线程占用,那么如果还有线程再次申请共享资源的时候,就会把这些获取不到资源(锁)的线程加入到一个队列当中,这一个队列就是CLH队列

下面,将详细说明一下CLH是什么


 CLH(Craig,Landin,and Hagersten)

 CLH队列是一个虚拟的双向队列。

 它把每一个请求共享资源的线程封装成一个CLH节点

 这一个节点(Node)拥有以下的属性:

 ①线程的引用(Thread thread);

 ②当前节点的前驱节点(Node prev); 

 ③当前节点的后继节点(Node next);

 ④当前节点在队列当中的状态(int waitStatus)

下图来源于网站javaguide.cn 

下图来源于网站:Java并发之AQS详解 - waterystone - 博客园 


AQS使用成员变量state表示同步状态

       举一个例子:以ReentrantLock为例,state的初始值为0,表示未锁定的状态。当有线程(A)调用lock()方法的时候,会调用tryAcquire()方法令这个state的值+1。此后,如果有其他线程(B)再次tryAcquire(),那么就会失败,然后被阻塞。


二、什么是信号量(Semaphore)

       信号量是用来控制同时访问特定资源线程的数量,它通过协调各个线程,以保证合理地使用公共资源。

       举一个我们生活当中很常见的例子:当进入地下停车场的时候,通常可以看到地下停车场显示"当前剩余车位数量"这样的提示字眼。

       每次有一辆车从入口进入地下停车场,那么可用车位-1

       如果有一辆车离开地下停车场,那么可用车位+1。

       信号量就有点类似于这个"计数器",描述了"可用资源的个数",也就是还可以停车停多少。


三、信号量的操作 

      信号量有两个操作,一个是P,另外一个是V。

      P操作:当申请一个可用资源的时候,计数器就要-1。acquire(表示申请资源)

      V操作:当释放一个可用资源的时候,计数器就要+1。release(表示释放资源)

      需要注意的是:当信号量变为0的时候,如果再次申请资源,那么就会发生阻塞等待。

      那么,Java当中的锁:synchronozed/ReentrantLock就可以理解为可用资源为1的信号量.

      因此,信号量也可以被当成锁来使用


 构造方法

      ①初始化一个信号量,指定可以获取资源的最大次数

 //初始化一个信号量,指定可以申请资源的最多的次数
 Semaphore semaphore=new Semaphore(10);

        此时,在Semaphore的构造方法当中,初始化的值为10.这个10。就是在AQS当中,所指定的state的值为多少。

        第一步:在构造方法当中,初始化一个permits的值,确定可以申请的最大的次数。

        


          第二步:在NonfairSync的构造方法当中,调用父类(Sync)的构造方法


   第三步:在Sync的构造方法当中,调用父类(AQS)的setState方法,确定state的个数    


②acquire()

        acquire()相当于申请一个访问资源,申请一次,就会减少一次可用申请的次数。  

        //初始化一个信号量,指定可以申请的最多的次数
        Semaphore semaphore=new Semaphore(10);
        //执行10次acquire操作之后就会阻塞
        for(int i=1;i<=14;i++){
            semaphore.acquire();
            System.out.println("这是第"+i+"次执行acquire");
        }

         可用看到,当执行了10次之后,线程就会阻塞等待.

         


       当调用acquire()方法的时候,线程会尝试获取许可证,如果此时AQS的state的值>=0的话,表示获取成功。获取成功之后,就采用CAS的方式令state的值-1

       如果state<0的话,表示许可数量不足,此时就会创建一个新的节点(Node)加入阻塞队列,也就是AQS当中。


  ③release()方法

        可以立即为,把刚才申请的资源重新释放掉。让这些资源可以再次被申请。

        因此,正确的做法为:使用了acquire()执行完某段任务之后,立刻执行release()释放资源。

        就好像去完商场购物之后,一定要把车开回家,而不是一直留在商场的地下停车场一个道理

        因此,正确的代码格式应当是这样的:

//初始化一个信号量,指定可以申请的最多的次数
        Semaphore semaphore=new Semaphore(11);
        //申请资源
        semaphore.acquire();
        //完成一些任务
        System.out.println("执行任务ing"+"....");
        //归还资源
        semaphore.release();

        调用release方法之后,线程释放许可证。

        步骤一、使用CAS的方式令state的值+1

        步骤二、唤醒AQS当中的一个线程(A)。

        步骤三、令唤醒的线程(A)继续去获取"许可证",也就是执行acquire()方法的逻辑。


四、CountDownLatch

       CountDownLatch类似于一个计时器。给定一个场景:在百米冲刺的赛道上面,发令枪一打响,然后裁判开始按下计时器。当运动员跑完的时候,裁判才会把计时器按停。

       假设现在,裁判的计时器新增了一个功能:当运动员没有跑完的时候,如果裁判按下了暂停,那么这个时候计时器就会阻塞裁判的手,直到运动员跑完,才允许暂停。

       那么,这个计时器就有点CountDownLatch


   构造方法       

    CountDownLatch指定了一个只含有一个参数的构造方法。

    当调用这个构造方法构造对象的时候,本质上也是指定AQS当中state的个数。 


countDown方法

       当有线程调用countDown方法的时候,其实本质上是以CAS的方式减少AQS当中state的个数。

       每调用一次countDown,就采用CAS的方式令state减少一次。需要注意的是,即使调用countDown方法的次数超过了构造方法当中指定的次数,也不会造成线程的阻塞。那么,什么情况下面线程会造成阻塞呢?下面将介绍一下await方法


await方法

当线程调用await方法的时候,如果AQS当中的state不为0,那么就需要阻塞等待。

如果state为0了,那么会在state为0的时候,释放所有等待的线程。 

可以看到await方法是可以中断的

 因此,如果线程因为state>0而阻塞等待。这个等待,是可以被中断的。


 本文部分内容转载自网站Java 面试指南 | JavaGuide(Java面试+学习指南)「Java学习指北+Java面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识。准备 Java 面试,复习 Java 知识点,首选 JavaGuide! icon-default.png?t=N0U7https://javaguide.cn/

     

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: AQS (AbstractQueuedSynchronizer) 是 Java 中的一种用于实现同步器的抽象类。它提供了一种通用的机制,用于实现同步工具(如锁、信号量和闭锁),而不需要编写底层同步代码。AQS 实现了一个队列,用于在多个线程之间安全地传递同步状态。 ### 回答2: AQS(AbstractQueuedSynchronizer,即抽象队列同步器)是Java中用于实现同步机制的基础类。它提供了一种在同步状态下等待/通知的机制,并支持独占和共享两种同步方式。 AQS基于一个先进先出(FIFO)的双向队列,被称为等待队列,来存储等待获取同步状态的线程。每个线程在申请获取同步状态时会被加入到等待队列的尾部,同时被阻塞。当同步状态可用时,只有队列头部的线程才能获取到同步状态,并被唤醒继续执行。 AQS采用了模板方法设计模式,提供了独占模式下的acquire和release方法以及共享模式下的acquireShared和releaseShared方法。具体的同步实现逻辑由子类来实现。 在AQS中,同步状态(state)是通过一个int类型的变量来表示,而具体的同步语义由子类中的实现方法来定义。AQS利用CAS(Compare and Swap)操作来保证同步状态的原子操作,这也是保证AQS实现的线程安全性的基础。 除了同步的基本功能,AQS还提供了一些扩展方法,如条件队列的支持,子类可以通过实现Condition接口来创建自己的条件队列。 总之,AQSJava中基于队列的同步控制机制的基础类,它通过一种等待/通知的机制实现线程间的同步和通信,提供了独占模式和共享模式的支持,是Java并发编程中非常重要的一个类。 ### 回答3: AQS (AbstractQueuedSynchronizer) 是 Java 中用于构建同步器的基础框架。它提供了一套简单且灵活的实现方式,可用于构建各种类型的同步器,如锁、信号量、倒计时门栓等。 AQS 的核心是一个等待队列,用于管理等待获取同步状态的线程。它通过内部的 node 对象来表示每个线程,并使用 CAS 操作来实现线程的安全操作。当一个线程需要获取同步状态时,它会在等待队列中插入一个 node,并进入自旋或阻塞等待其他线程的唤醒。当某个线程释放同步状态时,AQS 会将状态转移给队列中的下一个等待线程。 AQS 为具体的同步器提供了两种操作:获取同步状态和释放同步状态。获取同步状态的方式一般有两种:独占方式 (Exclusive) 和共享方式 (Shared)。独占方式是指同一时间只能有一个线程获取同步状态,如 ReentrantLock;共享方式是指多个线程可以同时获取同步状态,如 CountDownLatch。 AQS 的实现基于模板方法设计模式,使用了一个 state 成员变量来表示同步状态。具体的同步器需要继承并实现 AQS 的抽象方法,包括获取同步状态的方法 (tryAcquire、tryAcquireShared) 和释放同步状态的方法 (tryRelease、tryReleaseShared)。通过重写这些方法,可以定制实现特定的同步逻辑。 总而言之,AQSJava 中用于构建同步器的基础框架,通过等待队列和内部的 node 对象来管理线程的获取和释放同步状态。它提供了一套简单且灵活的实现方式,并支持独占和共享两种同步方式。通过继承并实现 AQS 的抽象方法,可以定制实现各种类型的同步器。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值