Java并发包下很多API都是基于AQS来实现的加锁和释放锁等功能的,AQS是java并发包的基础类。比如:ReentrantLock、ReentrantReadWriteLock底层都是基于AQS来实现的。
一、简介
AQS全名:AbstractQueuedSynchronizer,顾名思义就是抽象的队列式同步器,是除了java自带的synchronized关键字之外的锁机制,是并发容器下locks包内的一个类。它实现了一个FIFO(FirstIn、FisrtOut先进先出)的队列。底层实现的数据结构是一个双向链表。同时还存在一个重要的状态标识(int类型)。
二、原理
主要分为俩部分:双向队列部分和state状态标识。
双向队列原理:如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,此时就需要一套能进行线程阻塞等待以及线程被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
state状态标识:AQS使用一个 volaltile int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。AQS使用CAS对该同步状态进行原子操作实现对其值的修改。同时state在各个不同的同步组件中含义是不同的,比如:ReentrantLock中,stste表示获取锁的线程数,假如state=0,表示还没有线程获取锁,1表示有线程获取了锁。大于1表示重入锁的数量;而在countDownLatch中就是锁定的数量。
三、为啥底层是CAS和volatile实现的?
volatile:主要作用是实现线程之间的可见性,而状态标志位state需要保持各个线程之间的可见性,所以要使用volatile进行修饰。
CAS:因为在AQS中state标识是改变的,为了实现线程安全,保证一个头尾一一相连,就需要使用CAS实现线程安全。
四、ReentrantLock和AQS
当 一个线程尝试用ReentrantLock的lock()方法进行加锁时,这时AQS对象内部的核心的变量state会代表加锁的状态。初始状态下,这个state的值是0。另外,这个AQS内部还有一个关键变量,用来记录当前加锁的是哪个线程,初始化状态下,这个变量是null。之后AQS内部直接就是用CAS操作将state值从0变为1。
线程2过来也想获取这个锁(重入),发现state的值不是0,此时CAS操作将state从0变为1的过程会失败,因为state的值当前为1,说明已经有人加锁了!接着线程2会看一下,是不是自己之前加的锁,“加锁线程”这个变量明确记录了是线程1占用了这个锁,所以线程2此时就是加锁失败。此时就要将自己放入队列中来等待,等待线程1释放锁之后,自己就可以重新尝试加锁了。