1. AQS
AQS(AbstractQueuedSynchronizer)
是一个用来构建锁和同步器的框架,内部定义了很多锁的相关方法,使用 AQS 能简单高效的构造出大量的同步器,常见的 ReentrantLock
、ReentrantReadWriteLock
、CountDownLatch
、Semaphore
、SychronousQueue
等都是基于 AQS 的。
1.1 AQS 原理
AQS 内部维护了一个 volatile int state
来表示同步状态,通过内置的 FIFO 队列来完成获取资源线程的排队工作。AQS 使用 CAS 对改同步状态进行原子操作实现对其值的修改。
protected final int getState() {
return state;
}
protected final void setState(int newState) {
state = newState;
}
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
1.2 AQS 对资源的共享方式
Exclusive
独占,比如ReentrantLock
Share
共享,多个线程共享,比如Semaphore
、CountDownLatch
1.3 模板模式
- 同步器的设计是基于模板设计模式的,一般自定义同步器的方式:
- 继承 AQS,重写其指定方法
- 将 AQS 组合在自定义同步组件的实现中,调用其模板方法,这些模板方法会优先调用重写的方法
- 需要重写的 AQS 方法:
isHeldExclusively()
改线程是否正在独占资源tryAcquire(int)
尝试独占获取资源tryRelease(int)
尝试独占释放资源tryAcquireShared(int)
共享方式获取资源tryReleaseShared(int)
共享方式释放资源
- 这些方法在 AQS 里都是直接
throw new UnsupportedOperationException();
- 一般来说,同步器只需要实现独占的方法或者共享方法中的一种就够了,但是也支持两种方式,比如
ReentrantReadWriteLock
2. 同步器
2.1 Semaphore
信号量
- 允许多个线程同时访问
- 执行
acquire
阻塞,直到有一个许可证可以获得然后拿走一个许可证,也可以多个 release
方法是释放一个许可,可能会释放一个acquire
阻塞的线程,也可以多个- 主要是维持了一个可获得许可证的数量,常用于限制某资源的线程数量
Semaphore
的构造方法也可以设置公平和非公平模式
2.2 CountDownLatch
倒计时器
- 允许
count
个线程阻塞在一个地方,知道所有的线程执行完毕 countDown
方法其实就是调用tryReleaseShared
以 CAS 的方式减少state
,当state
为 0 时表示所有线程都执行力countDown
,所有阻塞的线程会继续执行下去state
不为 0 的时候,把线程放入阻塞队列,自旋检查state
值- 将
state
设为 n,适用于主线程等待其他线程然后一起执行 - 将
state
设为 1,其他都countDownLatch.await()
住,一执行countDown
多个线程同时执行,可以实现线程的并行性 CountDownLatch
是一次性的,只能再初始化的时候设置state
,没有其它方法能改变这个值
2.3 CyclicBarrier
循环栅栏
- 和
CountDownLatch
很类似,但是是基于ReetrantLock
和Condition
的 - 就是让一组线程到达一个屏障的时候被阻塞,直到最后一个线程到达这个屏障就释放所有
await
的线程 - 构造函数中可以添加
Runnable
,实现类似于回调函数的功能,当所有线程到达屏障时,执行方法 - 可以实现多线程计算,再统计的功能
CountDownLatch
重点是一个线程等待,其它线程在执行完某件事之后可以终止,也可以等待CyclicBarrier
重点是所有线程到达,阀门打开继续执行,一个线程没有完成其他都要等待
2.4 ReetrantLock
ReentrantReadWriteLock
相比于ReetrantLock
可以保证多线程同时读