什么是AQS
JUC包中的大多数同步器实现都是围绕着共同的基础行为,比如等待队列,条件队列,独占获取,共享获取,而这些行为的抽象就是基于AbstractQueueSynchronizer(AQS)实现的,即抽象队列同步器,采用模版方法模式封装共性的实现,可以用来实现一个依赖状态的同步器。
AQS具备的特性:
- 阻塞等待队列
- 共享/独占
- 公平/非公平
- 可重入
- 允许中断
JDK中提供的大多数的同步器如Lock, Latch, Barrier等,都是基于AQS框架来实现的
如何设计一把独占锁?
管程----Java同步的设计思想
- 管程:管理共享变量以及对共享变量的操作过程,让他们支持并发
- 互斥:同一时刻只允许一个线程访问共享资源
- 同步:线程之间如何进行通信、协作
MESA模型
在管程的发展史上,先后出现过三种不同的管程的模型,分别是Hasen模型、Hoare模型和MESA模型。现在正在广泛使用的是MESA模型。
管程中引入了条件变量的概念,而且每个条件变量都对应有一个等待队列。条件队列和等待队列的作用是解决线程之间的同步问题。
- 同步等待队列是用来针对互斥问题的。
- 条件等待队列,针对同步场景控制线程的顺序,没满足条件,让线程等待,满足条件从等待队列里面唤醒
Java中针对管程有两种实现:
- 一种是基于Object的Monitor机制,用于sychronized内置锁的实现
- 一种是抽象队列同步器AQS,用于juc包下的Lock锁机制
AQS核心结构
内部维护属性 volatile int state
- state表示资源的可用状态
三种访问方式:
- getState()
- setState()
- compareAndSetState()
定义了两种资源的访问方式:
- 独占式---Exclusive:只有一个线程能够执行,如ReentrantLock
- 共享式----Share:多个线程可以同时执行,如:CountDownLatch/Semophore
AQS实现时候主要实现以下几种方法:
- isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它
- tryAcquire(int):独占式方式,尝试获取资源,成功则返回true,失败则返回false
- tryRelease(int):独占方式,尝试释放资源成功则返回true,失败则返回false
- tryAcquireShared(int):共享方式,尝试获取资源。负数表示失败;0表示成功,但是没有剩余可用资源;正数表示成功,且有剩余资源
- tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待节点返回true,否则返回false.
2.3AQS定义两种队列
- 同步等待队列:主要用于维护获取锁失败时入队的线程
- 条件等待队列:调用await()的时候会释放锁,然后线程会加入到条件队列,等到某个条件获取后重新获得锁,调用signal()唤醒的时候会把条件队列中的线程节点移动到同步队列中,等待再次获取锁。