目录
2.3.1 AQS内部维护属性volatile int state
1. 管程
1.1 什么是管程
1.2 管程模型-MESA

2. AQS
2.1 什么是AQS
AQS是一个抽象同步框架,可以用来实现一个依赖状态的同步器。
java.util.concurrent包中的大多数同步器实现都是围绕着共同的基础行为,比如等待队列、条件队 列、独占获取、共享获取等,而这些行为的抽象就是基于 AbstractQueuedSynchronizer(简称 AQS) 实现的。![]()
JDK中提供的大多数的同步器如Lock, Latch, Barrier等,都是基于AQS框架来实现的 。
- 一般是通过一个内部类Sync继承 AQS
- 将同步器所有调用都映射到Sync对应的方法
以ReentrantLock为例:下图是ReentrantLock内部类Sync的继承关系![]()
2.2 AQS的特性
- 阻塞等待队列
- 共享/独占
- 公平/非公平
- 可重入
- 允许中断
2.3 AQS核心结构
2.3.1 AQS内部维护属性volatile int state
- state表示资源的可用状态
- State三种访问方式:
- getState()
- setState()
- compareAndSetState()
2.3.2 定义了两种资源访问方式
- Exclusive-独占,只有一个线程能执行,如ReentrantLock
- Share-共享,多个线程可以同时执行,如Semaphore/CountDownLatch(可参考常用并发工具类的使用及应用场景详解-优快云博客 中的Semaphore和CountDownLatch进行理解)
2.3.3 AQS实现时主要实现的方法
- isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
- tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
- tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
- tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表
- 示成功,且有剩余资源。
- tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回 false。
2.4 AQS定义两种队列
- 同步等待队列: 主要用于维护获取锁失败时入队的线程。
- 条件等待队列: 调用await()的时候会释放锁,然后线程会加入到条件队列,调用signal()唤醒的时候会把条件队列中的线程节点移动到同步队列中,等待再次获得锁。
AQS 定义了5个队列中节点状态:1. 值为0,初始化状态,表示当前节点在sync队列中,等待着获取锁。2. CANCELLED,值为1,表示当前的线程被取消;3. SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;4. CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;5. PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
2.4.1 同步等待队列
AQS 依赖CLH同步队列来完成同步状态的管理:
- 当前线程如果获取同步状态失败时,AQS则会将当前线程已经等待状态等信息构造成一个节点(Node)并将其加入到CLH同步队列,同时会阻塞当前线程.
- 当同步状态释放时,会把首节点唤醒(公平锁),使其再次尝试获取同步状态。
- 通过signal或signalAll将条件队列中的节点转移到同步队列。(由条件队列转化为同步队列)
2.4.2 条件等待队列
AQS中条件队列是使用单向列表保存的,用nextWaiter来连接:
- 调用await方法阻塞线程;
- 当前线程存在于同步队列的头结点,调用await方法进行阻塞(从同步队列转化到条件队列)
3. AQS的实现之 ReentrantLock 详解
3.1 ReentrantLock基本使用
public class ReentrantLockTest {
private final ReentrantLock lock = new ReentrantLock();
// ... 34
public void doSomething() {
lock.lock(); // block until condition holds 6
try {
// ... method body 8
} finally {
lock.unlock(); 1
}
}
}
3.2 ReentrantLock的主要源码解读
AQS的特性有阻塞等待队列 、共享/独占 、公平/非公平 、可重入 、允许中断,那么我们解读ReentrantLock时着重看一下这几个特性在ReentrantLock中是如何实现的。
3.2.1 创建锁 new ReentrantLock()
ReentrantLock的锁可以是公平锁也可以是非公平锁
从下图中ReentrantLock的构造方法可知,创建锁有两种:
- ReentrantLock() 非公平锁
- ReentrantLock(boolean fair) 公平锁
3.2.2 获取锁:lock()
非公平锁
如上述,如果ReentrantLock创建的是非公平锁,则实际创建的是他的内部类NonfairSync对象,则lock()调用的则是NonfairSync的lock()方法
compareAndSetState()返回true,执行setExclusiveOwnerThread(),其源码注释说明,CAS成功后,当前线程获取了独占锁。
compareAndSetState()返回false, 则进入acquire(1); 该方法则是获取锁的一些相关操作。
我们看以下tryAcquire这个方法,当前要获锁线程和已经占用锁的线程是同一个锁时,不会死锁,而是给这个锁的状态加一,以下代码实现了AQS的可重入性这个特点
我们再看一下这个lock()中compareAndSetState()方法,其实就是一个原子操作,当前线程更改该state,更改成功,返回true.
公平锁
如上述,如果ReentrantLock创建的是公平锁,则实际创建的是他的内部类FairSync对象,则lock()调用的则是FairSync的lock()方法,这个lock直接调用了acquire()方法。
总结:
通过上述公平锁与非公平锁获取的阐述,我们发现非公平锁获取锁时,多了一步CAS去修改state,也就是说,当一个线程进来时,多了一次直接抢占到锁的机会。如果没有抢到,后续和公平锁一样,进入队列等待。
当要获取锁的线程和占有锁是同一个线程时,状态值加1,实现了可重入性。
每次只容许一个线程进入,是一个独占锁。
当没有获取到锁,会被挂起。
3.2.3 unlock()
ReentrantLock的unlock方法实际调用的是AbstractQueuedSynchronizer已经定义好的release方法。
Sync继承了AbstractQueuedSynchronizer。