J.U.C 之 AQS
AbStractQueuedSynchronizer
类,简称AQS
,是一个来构建锁和同步器的框架,JDK1.5
开始引入了J.U.C
,大大提高了JAVA程序的并发性,而AQS
则是J.U.C
的外围,是并发类中的外围局部,他是一个基于FIFO
队列,这个队列能够构建锁或其它相干的同步根底框架
AQS
底层构造
底层采纳双向链表,是队列的一种实现,因而能够当做是一个队列。其中Sync queue即同步队列,它是双向链表,包含hean结点(次要用作后续的调度)与tail结点。Condition queue不是必须的,单向链表,只有在须要应用到condition的时候才会存在这个单向链表,并且可能存在多个Condition queue
- 应用Node实现FIFO队列,能够用于构建锁或者其余同步安装的根底框架
- 利用了一个int类型示意状态。在AQS中,存在一个state成员变量,基于AQS有一个同步组件ReentrantLock,在这个组件中,state示意获取锁的线程数,如果state == 0示意无线程获取锁,state == 1示意已有线程获取锁,state > 1示意锁的数量
- 应用办法是继承。AQS的设计是基于模板办法,应用须要继承AQS,并覆写其中的办法。
- 子类通过继承并通过实现它的办法治理其状态{acquire() 和 release()}的办法操纵状态
- 能够同时实现排它锁和共享锁模式(独占、共享)。它的所有子类中,要么实现并应用它的独占性能API,要么实现共享锁的性能,而不会同时应用两套API。即使是它比拟有名的子类ReentrantReadWirteLock也是通过两个外部类读锁和写锁别离应用两套API实现的。AQS在性能上,有独占管制和共享管制两种性能。
- 在LOCK包中的相干锁(罕用的有ReentrantLock、 ReadWriteLock)都是基于AQS来构建.然而这些锁都没有间接来继承AQS,而是定义了一个Sync类去继承AQS,因为锁面向的是应用用户,而同步器面向的则是线程管制,那么在锁的实现中聚合同步器而不是间接继承AQS就能够很好的隔离二者所关注的事件.
基于以上设计,AQS具体实现的大抵思路
AQS外部保护了一个CLH队列来治理锁,线程首先会尝试获取锁,如果失败,会将以后线程以及期待状态等信息包装成Node结点退出同步队列(Sync queue)中。接着一直循环尝试获取锁,条件是以后结点为head间接后继才会尝试,如果失败则会阻塞本人,直到本人被唤醒;而当持有锁的线程,开释锁的时候,会唤醒队列中后继线程。基于这些根底的设计和思路,JDK提供了许多基于AQS的子类。
独占式锁过程总结
AQS的模板办法acquire通过调用子类自定义实现的tryAcquire获取同步状态失败后->将线程结构成Node节点(创立一个独占式节点 )(addWaiter)->将Node节点增加到同步队列对尾(addWaiter)->节点以自旋的办法获取同步状态(acquirQueued)。在节点自旋获取同步状态时,只有其前驱节点是头节点的时候才会尝试获取同步状态,如果该节点的前驱不是头节点或者该节点的前驱节点是头节点单获取同步状态失败,则判断以后线程须要阻塞,如果须要阻塞则须要被唤醒过后才返回。在开释同步状态时,同步器调用tryRelease(int arg)办法开释同步状态,而后唤醒头节点的后继节点。
共享式锁过程总结
共享式获取与独占式获取的最次要区别在于同一时刻是否有多个线程同时获取到同步状态。通过调用acquireShared(int arg)办法能够共享式得获取同步状态。
同步器调用tryAcquireShared(int arg)办法尝试获取同步状态,其返回值为int类型,当返回值大于0时,示意可能获取同步状态。因而,在共享式获取的自旋过程中,胜利获取同步状态并且退出自旋的条件就是tryAcquireShared(int arg)办法返回值大于等于0。共享式开释同步状态状态是通过调用releaseShared(int arg)办法
CountDownLatch、ReentrantReadWriteLock、Semaphore等都是共享式获取同步状态的。
关注微信公众号:【入门小站】解锁更多知识点
发表回复