【杂记】AQS同步器

1.CAS自旋实现的轻量级锁的存在哪些问题?

(1)CAS恶性空自旋会浪费大量的CPU资源;
(2)在SMP架构的CPU上会导致"总线风暴";

2.解决CAS恶性空自旋的方法有哪些?

主要有效的方式之一是:以空间换时间,较为常见的方法有两种:分散操作热点和使用队列削峰。JUC并发包使用的是队列削峰的方案解决CAS的性能问题,并提供了一个基于双向队列的削峰基类——抽象基础类AbstractQueuedSynchronizer(抽象同步器类,简称为AQS)

3.锁与队列都有什么关系?

无论是单体服务应用内部的锁,还是分布式环境下多体服务应用所使用的分布式锁,为了减少由于无效争夺导致的资源浪费和性能恶化,一般都基于队列进行排队与削峰。

4.常见的内部队列有哪些?

(1)CLH锁的内部队列,是一个单向队列,也是一个FIFO队列,在独占锁中,竞争资源在一个时间点只能被一个线程锁访问,队列头部的节点表示占有锁的节点,新加入的抢锁线程则需要等待,会插入队列的尾部;
(2)分布式锁的内部队列,常见的是基于队列的方式进行不同节点中"等待线程"的统一调度和管理。
(3)AQS的内部队列,是CLH队列的一个变种,主要原理和CLH队列差不多,AQS队列内部维护的是一个FIFO的双向链表,这种结构的特点是每个数据结构都有两个指针,分别指向直接的前驱节点和直接的后继节点,所以双向链表可以从任意一个节点开始很方便地访问到前驱节点和后继节点。每个节点其实由线程封装的,当线程争抢锁失败后会封装成节点加入AQS队列中;当获取锁的线程释放锁以后,会从队列中唤醒一个阻塞的节点(线程)。

5.AQS的核心成员

(1)状态标志位 state ,AQS中维持了一个单一的volatile修饰的状态信息state,可以理解为锁的同步状态。
(2)队列节点类,内部类Node定义,AQS是一个虚拟队列,不存在队列实例,仅存在节点之间的前后关系。内部类的属性主要有以下几种:
(2.1)waitStatus属性,每个节点与等待线程关联,每个节点维护一个状态waitStatus,(1)static final int CANCELLED=1waitStatus值为1时表示该线程节点已释放(超时、中断),已取消的节点不会再阻塞,表示线程因为中断或者等待超时,需要从等待队列中取消等待。
(2.2)static final int SIGNAL=?1waitStatus为SIGNAL(-1)时表示其后继的节点处于等待状态,当前节点对应的线程如果释放了同步状态或者被取消,就会通知后继节点,使后继节点的线程得以运行。
(2.3)static final int CONDITION=?2waitStatus为-2时,表示该线程在条件队列中阻塞(Condition有使用),表示节点在等待队列中(这里指的是等待在某个锁的CONDITION上,关于CONDITION的原理后面会讲到),当持有锁的线程调用了CONDITION的signal()方法之后,节点会从该CONDITION的等待队列转移到该锁的同步队列上,去竞争锁(注意:这里的同步队列就是我们讲的AQS维护的FIFO队列,等待队列则是每个CONDITION关联
的队列)
(2.4)static final int PROPAGATE=?3waitStatus为-3时,表示下一个线程获取共享锁后,自己的共享状态会被无条件地传播下去,因为共享锁可能出现同时有N个锁可以用,这时直接让后面的N个节点都来工作。这种状态在CountDownLatch中使用到了。(5)waitStatus为0waitStatus为0时,表示当前节点处于初始状态。Node节点的waitStatus状态为以上5种状态的一种。
(3)thread成员 Node的thread成员用来存放进入AQS队列中的线程引用;Node的nextWaiter成员用来指向自己的后继等待节点,此成员只有线程处于条件等待队列中的时候使用。
(4)抢占类型常量标识,Node节点还定义了两个抢占类型常量标识:SHARED和EXCLUSIVE,具体如下:SHARED表示线程是因为获取共享资源时阻塞而被添加到队列中的;EXCLUSIVE表示线程是因为获取独占资源时阻塞而被添加到队列中的。

6.AOS的FIFO双向队列是如何进行的?

AOS通过内置的FIFO双向队列来完成线程的排队工作,内部通过节点head和tail记录队首和队尾元素,元素的节点类型为Node类型,AOS的首节点和尾节点都是懒加载。只有线程竞争失败的情况下,有新线程加入同步队列,AOS才创建一个head节点,head节点只能被setHead()方法修改,并且节点的waitStatus不能为0.尾节点只有在新线程阻塞时才被创建。

7.RenntrantLock与AQS的组合关系?

RenntrantLock显式锁操作是委托给一个Sync内部类的实例来完成。而Sync内部类只是AQS的一个子类,所以本质上RenntrantLock的显式锁操作是委托给AQS完成的,一个ReentrantLock对象的内部一定有一个AQS类型的组合实例,二者之间是组合关系。

8.了解过模板模式吗?它有何优势?

模板模式是类的行为模式。准备一个抽象类,将部分逻辑以具体方法的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类提供不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。模板模式的关键在于:父类提供框架性的公共逻辑,子类提供个性化的定制逻辑。
模板模式的优点如下:1.通过算法骨架最大限度地进行了代码复用,减少重复代码。2·模板模式提取了公共部分代码,便于统一维护。3·钩子方法是由子类实现的,因此子类可以通过拓展增加复杂的功能,符合开放封闭原则。

9.在AQS里面采用了模板模式,主要重写了哪些方法?

1·tryAcquire(int):独占锁钩子,尝试获取资源,若成功则返回true,若失败则返回false。
2·tryRelease(int):独占锁钩子,尝试释放资源,若成功则返回true,若失败则返回false。
3·tryAcquireShared(int):共享锁钩子,尝试获取资源,负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
4·tryReleaseShared(int):共享锁钩子,尝试释放资源,若成功则返回true,若失败则返回false。
5·isHeldExclusively():独占锁钩子,判断该线程是否正在独占资源。只有用到condition条件队列时才需要去实现它。

10.AQS节点的自旋入队有哪几种情况?

(1)如果AQS的队列非空,新节点入队的插入位置在队列的尾部,并且通过CAS方式插入,插入之后AQS的tail将指向新的尾节点。
(2)如果AQS的队列为空,新节点入队时,AQS通过CAS方法将新节点设置为头节点head,并且将tail指针指向新节点。
11.了解过节点出队的方法吗?
节点出队的算法在acquireQueued()方法中,这是一个非常重要的模板方法。acquireQueued()方法不断在前驱节点上自旋(for死循环),如果前驱节点是头节点并且当前线程使用钩子方法tryAcquire(arg)获得了锁,就移除头节点,将当前节点设置为头节点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值