资料内容:
1、什么是AQS
在 Java 中, AQS 是 AbstractQueuedSynchronizer 的简称,直译过来是抽象队列
同步器。 AbstractQueuedSynchronizer 是一个提供了基于 FIFO 等待队列实现的同步器
框架,是 Java 并发库中锁和同步器的核心实现之一。它允许开发人员通过继承
AQS 类来实现自
定义同步器,从而为多线程程序提供可靠的同步机制。
AQS 的核心思想是,将等待共享资源的线程封装在一个 FIFO 队列中,然后用 CAS 操
作等原子操作来修改该队列中的头结点和尾结点。对于独占式同步器(例如
ReentrantLock ), AQS 还提供了一个 state 变量,用于记录当前占用该同步器的线程
数。每次执行 acquire 操作时,线程会尝试获取同步器的状态。如果成功获取,则该线程可以继
续执行;否则,需要一定的阻塞等待唤醒机制来保证锁的分配, AQS 中会将竞争共享资源失败的线
程添加到一个变体的 CLH 队列中。
1 public abstract class AbstractQueuedSynchronizer
2 extends AbstractOwnableSynchronizer implements
java.io.Serializable {
3 // CLH 变体队列头、尾节点
4 private transient volatile Node head;
5 private transient volatile Node tail;
6 // AQS 同步状态
7 private volatile int state;
8 // CAS 方式更新 state
9 protected final boolean compareAndSetState(int expect, int
update) {
10 return unsafe.compareAndSwapInt(this, stateOffset, expect,
update);
11 }
12 }
简单来说, AQS 是Java中的一个抽象类,为开发者提供了一种非常灵活的同步机制,可
以适用于多种场景,相比较于传统的 synchronized 关键字更加高效和可定制化。
2、谈谈CLH队列
CLH(Craig、Landin and Hagersten) 队列,是 单向链表实现的队列。申请线程只在本
地变量上自旋,它不断轮询前驱的状态,如果发现 前驱节点释放了锁就结束自旋,其主要有以下特
点:
1. CLH 队列是一个单向链表,保持 FIFO 先进先出的队列特性;独占锁
共享锁
独占锁
不可共存
不可共存
共享锁
不可共存
可共存
2. 通过 tail 尾节点(原子引用)来构建队列,总是指向最后一个节点;
3. 未获得锁节点会进行自旋,而不是切换线程状态;
4. 并发高时性能较差,因为未获得锁节点不断轮训前驱节点的状态来查看是否获得锁。
AQS 中的队列是 CLH 变体的虚拟双向队列,通过将每条请求共享资源的线程封装成一个节
点来实现锁的分配,相对于普通的 CLH 队列来说,其主要有以下特点:
1. AQS 中队列是个双向链表,也是 FIFO 先进先出的特性;
2. 通过 Head 、 Tail 头尾两个节点来组成队列结构,通过 volatile 修饰保证可
见性;
3. Head 指向节点为已获得锁的节点,是一个虚拟节点,节点本身不持有具体线程;
4. 获取不到同步状态,会将节点进行自旋获取锁,自旋一定次数失败后会将线程阻塞,相对
于 CLH 队列性能较好。
3、独占锁与共享锁
独占锁也叫排它锁,是指该锁一次只能被一个线程所持有,如果别的线程想要获取锁,只有等到
持有锁线程释放。获得排它锁的线程即能读数据又能修改数据,与之对立的就是共享锁。
共享锁是指该锁可被多个线程所持有。如果线程T对数据A加上共享锁后,则其他线程只能对A再
加共享锁,不能加排它锁。获得共享锁的线程只能读数据,不能修改数据。