AQS 的全称为(AbstractQueuedSynchronizer),这个类在 java.util.concurrent.locks 包下面。
AQS 是一个用来构建锁和同步器的框架,使用 AQS 能简单且高效地构造出应用广泛的大量的同步器, 比如我们提到的 ReentrantLock,Semaphore,其他的诸如 ReentrantReadWriteLock,SynchronousQueue,FutureTask(jdk1.7) 等等皆是基于 AQS 的。当然,我们自己也能利用 AQS 非常轻松容易地构造出符合我们自己需求的同步器。
AQS的实现依赖于内部的同步队列,它是一个FIFO双向队列,如果当前线程获取同步状态失败,AQS会将该线程以及等待状态等信息构造成一个Node,将其加入同步队列的尾部,同时阻塞当前线程,当同步状态释放时,唤醒队列的头节点。
1、获取同步状态
假设线程A要获取同步状态,初始状态state=0,线程A可以顺利获取锁,A获取锁后将state置为1。在A没有释放锁期间,线程B来获取锁,此时因为state=1,锁被占用,所以将B的线程信息和等待状态等数据构成一个Node节点,放入同步队列中,head和tail分别指向队列头部和尾部(此时队列中有一个空的Node节点作为头点,head指向这个空节点,空Node的后继节点是B对应的Node节点,tail指向它),同时阻塞线程B,阻塞使用的是LockSupport.park()方法。后续如果还有线程要获取锁,都会加入队列尾部并阻塞。
2、释放同步状态
当线程A释放锁时,将state置为0,此时线程A会唤醒头节点的后继节点,唤醒其实是调用LockSupport.unpark(B)方法,即线程B从LockSupport.park()方法返回,此时线程B发现state已为0,所以线程B可以顺利获取锁,线程B获取锁后,B的Node节点出队。
总结:
AQS 内部有一个核心变量 —— state,是int类型,代表了加锁的状态,初始状态下,这个值为 0,另外还有一个关键变量,用来记录当前加锁的是哪个线程,初始化状态下,为null。
加锁以后的原理图:接着线程1来调用ReentrantLock 的 lock() 方法尝试进行加锁,这个加锁过程就是用CAS操作将 state 值从0变成1,一旦线程1加锁成功,就可以设置当前加锁线程是自己。
state: 所有线程通过通过CAS尝试给state设值,当state>0时表示被线程占用;同一个线程多次获取state,会叠加state的值,从而实现了可重入;
exclusiveOwnerThread: 在独占模式下该属性会用到,当线程尝试以独占模式成功给state设值,该线程会把自己设置到exclusiveOwnerThread变量中,表明当前的state被当前线程独占了;
CLH队列(FIFO —— 等待队列(同步队列): 等待队列中存放了所有争夺state失败的线程,是一个双向链表结构。state被某一个线程占用之后,其他线程会进入等待队列;一旦state被释放(state=0),则释放state的线程会唤醒等待队列中的线程继续尝试cas设值state;
head: 指向等待队列的头节点,延迟初始化,除了初始化之外,只能通过setHead方法进行修改;
tail: 指向等待队列的队尾,延迟初始化,只能通过enq方法修改tail,该方法主要是往队列后面添加等待节点。
三、ReentrantLock 公平锁和非公平锁原理非公平锁在调用 lock 后,首先就会调用 CAS 进行一次抢锁,如果这个时候恰巧锁没有被占用,那么直接就获取锁返回,
非公平锁在 CAS 失败后,和公平锁一样都会进到 tryAcquire 方法,在tryAcquire 方法中,如果发现锁这个时候被释放了(state == 0),非公平锁会直接CAS抢锁,但是公平锁会判断等待队列是否有线程处于等待状态,如果有则不去抢锁,排队到队列后面。
四、AQS典型实现类和资源共享方式 五、AQS具体实现
同步器在实现的时候只需要实现共享资源state的获取和释放方式即可,具体线程等待队列的维护,AQS已经实现,同步器实现的时候主要实现下面几个方法:



