AQS介绍
AbstractQueuedSynchronizer,AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效地构造出应用广泛的大量的同步器。
AQS 原理
AQS维护一个共享资源state,通过内置的FIFO来完成获取资源线程的排队工作。(这个内置的同步队列称为"CLH"队列)。该队列由一个一个的Node结点组成,每个Node结点维护一个prev引用和next引用,分别指向自己的前驱和后继结点。AQS维护两个指针,分别指向队列头部head和尾部tail。
其实就是个双端双向链表。
AQS是JUC中很多同步组件的构建基础,简单来讲,它内部实现主要是状态变量state和一个FIFO队列来完成,同步队列的头结点是当前获取到同步状态的结点,获取同步状态state失败的线程,会被构造成一个结点(或共享式或独占式)加入到同步队列尾部(采用自旋CAS来保证此操作的线程安全),随后线程会阻塞;释放时唤醒头结点的后继结点,使其加入对同步状态的争夺中。
总结下获取锁的流程:
调用自定义同步器的tryAcquire()尝试直接去获取资源,如果成功则直接返回;
没成功,则addWaiter()将该线程加入等待队列的尾部,并标记为独占模式;addWaiter()的操作过程是首先尝试快速方式直接放到等待队列的队尾
acquireQueued()使线程在等待队列中休息,有机会时(轮到自己,会被unpark())会去尝试获取资源。获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false。
如果线程在等待过程中被中断过,它是不响应的。只是获取资源后才再进行自我中断selfInterrupt(),将中断补上。
AQS相关的并发类
CountDownLatch 用来解决一个线程等待多个线程的场景,旅游团团长必须等待所有游到齐才能出发去下一个景点;计数器不能循环使用(案例,通过两个线程池分别获取未对账订单和派送单,最后在主线程之中完成对账操作)
CyclicBarrier 用来解决一组线程之间相互等待,几个驴友之间不离不弃;计数器可以循环使用,同时也可以设置回调函数
Lock和Condition
Lock和Condition相比于JDK中synchronized的优势在于
1 能够响应中断
2 支持超时
3 支持非阻塞地获取锁 ,如果尝试获取锁失败,并不进入阻塞状态,而是直接返回
4 支持公平和非公平方式获取锁
我们可以通过condition的signal,await,signalAll,实现条件等待
Semaphore 可以控制指定数量的线程获取资源
核心是一个计数器 一个等待队列 和三个方法
init( ) down( ) up( )
信号量模型里面,down()、up() 这两个操作历史上最早称为 P 操作和V 操作,所以信号量模型也被称为 PV 原语。
ReadWriteLock
读读不互斥,读写互斥,写写互斥 适合于读多写少的场景
获取写锁的前提是读锁和写锁均未被占用
获取读锁的前提是没有其他线程占用写锁
ReadWriteLock 不允许锁的升级,只允许锁的降级,如果先持有读锁,随后再尝试获取写锁,那么线程将被永久阻塞;如果先持有写锁,在持有读锁,再释放写锁,此时线程将会依旧持有写锁。