AQS(AbstractQueuedSynchronizer)
AQS 提供了一个基于先入先出队列实现的线程同步器的基础框架。ReentrantLock、CountDownLatch、CyclicBarrier、Semaphore 都是基于 AQS 实现的。
AQS 提供两种资源共享方式
- 互斥访问:任何时候只能存在一个线程访问该共享资源,又可分为公平锁与非公平锁,AQS 默认采用公平策略。
- 共享访问:需要设置可同时访问共享资源的最大线程数。
核心设计
-
int 类型的 state 变量表示同步状态。使用 volatile 关键字修饰,实现线程的可见性。AQS 使用 CAS 对该同步状态进行原子操作实现对其值的修改。
-
FIFO 线程等待队列(CLH)。 当所有可用资源都被占用时,对于不能访问该共享资源的线程,需要阻塞排队等待,AQS 使用 CLH 队列锁来实现的(CLH 队列是一个虚拟的双向队列,即不存在队列实例,仅存在结点之间的关联关系)。
-
线程状态的控制。使用 LockSupport 工具类的用于阻塞和唤醒线程的相关方法来对线程的状态进行控制。
-
自旋和 CAS 机制实现无锁化的线程安全操作。在队列中增删线程节点,或者更新线程节点的状态 waitStatus 时,CAS 和自旋来实现更新和写入操作。
ReentrantLock 可重入锁
支持可重入性,表示能够对共享资源重复加锁,即当前线程再次获取该锁时不会阻塞。
state 等于 0,表示当前没有任何线程占用这个锁,state 大于 0,表示当前存在线程占用这个锁,此时其他线程不能获得锁,而当前成果加锁的线程是可用多次访问使用该 ReentrantLock 对象加锁的其他方法或代码块,每访问一次,state递增 1,实现可重入。
ReentrantLock 功能与 synchronized 关键字类似,但灵活性更好,提供了更多的方法(支持响应中断、超时、尝试获取锁)。
ReentrantLock支持公平锁和非公平锁。
ReentrantReadWriteLock 可重入读写锁
ReentrantReadWriteLock 包含读写两把锁,实现并发读的功能,
- 对于同一线程而言,读读、写写、写读是共享的,读写是互斥的。
- 对不同线程而言,读是共享的,读写、写读、写写是互斥的。
CountDownLatch 倒计时同步器
CountDownLatch 主要提供了对多个线程进行协调、控制的作用,可以在主线程等待所有子线程的执行完成。
将状态量 state 定义为倒计时的数值,子线程调用 countDown 方法递减 state,主线程调用 await 方法阻塞等待 state 到 0。
CyclicBarrier 循环栅栏同步器
CyclicBarrier 是一个可循环使用的线程同步器。可以理解为一个栅栏,拦住线程,等到所有的线程都到达则打开栅栏让所有线程继续执行。
相对于 CountDownLatch,CyclicBarrier 可以重复执行,即可以调用 reset 方法,将计数器重置为初始值,同时,可以指定一个 Runnable 任务在栅栏打开时执行。
- CountDownLatch : 一个或者多个线程,等待其他多个线程完成某件事情之后才能执行
- CyclicBarrier : 多个线程互相等待,直到到达同一个同步点,再继续一起执行。
CyclicBarrier 是基于 ReentrantLock 和 Condition 来实现的
Semaphore 信号量同步器
Semaphore 主要用于控制资源的可用数量,对于线程优先级方面,存在公平和非公平两种实现,默认为非公平实现。
通过设置同步状态量 state 来定义资源的可用量。
- 对于公平实现,需要先检查是否有其他线程等待,若有,则进入等待队列。
- 对于非公平实现,线程直接尝试访问一次资源,若失败则放入等待队列。