共计 1752 个字符,预计需要花费 5 分钟才能阅读完成。
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 等都是共享式获取同步状态的。
关注微信公众号:【入门小站】解锁更多知识点