深入理解AQS(抽象队列同步器)

一、什么是AQS

AQS:用来构建锁或其他同步器组件的重量级基础框架及整个JUC体系的基石,通过内置的FIFO队列来完成资源获取线程的排队工作,并通过一个int类型变量表示持有锁的状态。如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁的分配。主要通过CLH队列的变体实现,将暂时获取不到锁的线程加入到队列中,这个队列就是AQS抽象的表现。它将请求共享资源的线程封装成队列的节点(Node),通过CAS自旋以及LockSupport.park()的方式,维护state变量的状态,使并发达到同步的控制效果。

CLH队列:Craig、Landin and Hagersten队列,是一个单向链表,AQS中的队列是CLH变体的虚拟双向队列,虚拟的双向队列即不存在队列实例,仅存在节点之间的关联关系。

java.util.concurrent.locks.AbstractOenableSynchronizer;

解释:是用来构建锁或者其他同步器组件的重量级基础框架及整个JUC体系的基石,通过内置的FIFO(先进先出)队列来完成资源获取线程的排队工作,并通过一个int类变量表示持有锁的状态

 

 和AQS有关的类:

ReentrantLock:

 CountDownLatch

ReentrantReadWriteLock:

Semaphore:

锁和AQS的关系:锁面向锁的使用者,定义了程序员和锁交互的使用层API,隐藏了实现细节

同步器AQS是锁的实现

二、AQS源码分析

AQS使用一个volatile的int类型的成员变量来表示同步状态,通过内置的 FIFO队列来完成资源获取的排队工作将每条要去抢占资源的线程封装成 一个Node节点来实现锁的分配,通过CAS完成对State值的修改。

AQS的同步状态state成员变量  零说明没有资源占用,自由状态可以办理  大于等于1 有线程占用资源

AQS的CLH队列

AQS同步队列的基本结构


                         AQS底层是怎么排队的 :是用LockSupport.park()来进行排队的

三、通过ReentrantLock中的非公平锁进行分析

主要是通过以下几个方法进行实现的:

通过银行办理业务的例子来深入了解:

  三个线程A,B,C来银行窗口办理业务,服务窗口每次只能服务一个人,初识的时候窗口是没人的!

lock()

  这边线程A拿到了去窗口办理业务的机会,通过compareAndSetState比较并交换,将AQS中变量state设置成1

acquire() 

  线程A已经占用了窗口,线程B只能走acquire这个方法

 tryAcquire()



  


 在来看acquire() 的方法,B线程返回false,取反的是true,往下面看下一个方法

addWaiter(Node.EXCLUSLVE)

  完成B节点的入队,通过enq源码可以知道,B线程第一次并没有进行入队,到第二次循环才将B线程入队返回

acquireQueued(addWaiter(Node.EXCLUSIVE), arg)

B抢成功了去去窗口办理业务,抢占不成功就在等待,直到被唤醒。

 unlock();

A 办完业务,准备离开,B上位

原文地址:https://www.cnblogs.com/zhangzhixi/p/14811475.html