AQS
AQS的地位相比于学习Java必须懂JVM相同,JUC的大部分类的底层通过AQS实现。
1. 简介
AbstractQueuedSynchronizer简称AQS, 字面翻译为抽象队列同步器,是用来实现锁或者其它同步器组件的公共基础部分的抽象实现,是重量级基础框架及整个JUC体系的基石,主要用于解决锁分配给"谁"的问题。表示持有锁的状态,整体就是一个抽象的FIFO队列来完成资源获取线程的排队工作,并通过一个int类变量表示持有锁的状态。
1.1 CLH队列
CLH队列命名来自三个科学家首字母Craig、Landin and Hagersten,AQS中的队列是CLH队列的变种,是一个虚拟的双向FIFO队列。
1.2 JUC的基石
在ReentrantLock、CountDownLatch、ReentrantReadWriteLock、Semaphore、ThreadPoolExecutor等中使用了使用AQS:
1.3 锁和AQS关系
锁:面向开发者。
AQS: 面向锁的实现者。
Java并发大神DougLee,提出统一规范并简化了锁的实现,将其抽象出来。屏蔽了同步状态管理、同步队列的管理和维护、阻塞线程排队和通、唤醒机制等,是一切锁和同步组件实现的公共基础部分
2. 排队等候机制
抢到资源的线程直接使用处理业务,抢不到资源的必然涉及一种排队等候机制。抢占资源失败的线程继续去等待(类似银行业务办理窗口都满了,时没有受理窗口的顾客只能去候客区排队等候),但等候线程仍然保留获取锁的可能且获取锁流程仍在继续(候客区的顾客也在等着叫号,轮到再去受理窗口办理业务)。
既然说到了排队等候机制,那么就一定会有某种队列形成,这样的队列是什么数据结构呢?
如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁分配。这个机制主要用的是CLH队列的变体实现的,将暂时获取不到锁的线程加入到队列中,这个队列就是AQS同步队列的抽象表现。它将要请求共享资源的线程及自身的等待状态封装成队列的结点对象(Nod),通过CAS、自旋以及LockSupport.park()
的方式,维护state变量的状态,使并发达到同步的效果。
3. AQS属性成员
AQS中保存的对象是Node, 每个线程信息会被封装成Node对象,AQS的状态state: 在Node对象中,由于是双向队列,所以提供head和tail,Node中status属性表示线程状态:
AQS使用一个volatile的int类型的成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作将每条要去抢占资源的线程封装成一个Node节点来实现锁的分配,通过远AS完成对State值的修改。因此整个AQS结构如下: