JUC核心-AQS(AbstractQueuedSynchronizer)简析

AQS是什么?

  • AQS 全称是 AbstractQueuedSynchronizer, 它提供一种依赖于FIFO等待队列的构建锁和同步器的框架

  • CAS是什么?

CAS(Compare And Swap),即比较并交换。是解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在CAS指令之前返回该位置的值。CAS有效地说明了“我认为位置V应该包含值A;如果包含该值,则将B放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。

底层实现

整体结构

  • AQS 的结构大概可总结为以下 3 部分:

    • 用 volatile 修饰的整数类型的 state 状态,用于表示同步状态,提供 getState 和 setState, compareAndSetState来操作同步状态;
    • 提供了一个 FIFO 等待队列,实现线程间的竞争和等待,这是 AQS 的核心;其中, 链表头Head和链表尾Tail也有volatile修饰。
    • AQS 内部提供了各种基于 CAS 原子操作方法,如 compareAndSetState 方法,并且提供了锁操作的acquire和release方法。
  • 提供两种锁的默认实现方式:

    • 独占锁(Exclusive)
    • 共享锁(Shared)
  • tryAcquire*, tryRelease* 都是需要实现类自己去实现的方法, 如果不实现的话,是会抛出异常UnsupportedOperationException的

  • 用到的设计模式-模板模式, 在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。

独占锁

acquire获取独占锁

  • 伪代码实现
 while (!tryAcquire(arg)) {
    <em>enqueue thread if it is not already queued</em>;
    <em>possibly block current thread</em>;
 }
  • 代码实现
    • 先尝试获取锁,获取成功则成功
    • 尝试失败,则把当前线程包装成为一个节点,然后等待获取的机会
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
  • NOTE这里有必要说明一下,就是当一个节点成为head节点的时候,他不一定会是下一个获取锁的节点,从上面代码也可以看出来,所以获取锁的线程都会先尝试获取锁一次,这样有可能等待队列的头节点也可能获取锁失败。

release释放独占锁

  • 伪代码实现
  if (tryRelease(arg))
     <em>unblock the first queued thread</em>;
  • 代码实现
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

共享锁

acquireShared获取共享锁

  • 代码实现
public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}

releaseShared释放共享锁

  • 代码实现
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

FIFO队列

队列模型

这个在AQS的注释说明里边,Doug Lea已经说的很明确了,还做了图解

  • 这个等待队列是CLH(Craig, Landin, and Hagersten)队列的一个变种,这种队列常被用作自旋锁(这个概念就不展开了)
  • 作用:用于阻塞同步器
  • 结构
         +------+  prev +-----+       +-----+
 head    |      | <---- |     | <---- |     |  tail
         +------+       +-----+       +-----+
  • 入队:插入队尾
  • 出队:直接设置head即可

节点

  • 节点其实是把想要获取锁的线程包装了一番
//mode分exclusive和shared两种模式
new Node(Thread.currentThread(), mode);
  • 节点状态
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED =  1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL    = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
 * waitStatus value to indicate the next acquireShared should
 * unconditionally propagate. 
 * 这个状态只在共享锁的模式下有效,这个传播的用处在哪儿呢?
 * 举例说明:读写锁,写读操作和写写操作互斥,读读之间不互斥;当调用acquireShared获取读
 * 锁时,会检查后续节点是否是获取读锁,如果是,则同样释放;
 */
static final int PROPAGATE = -3;

常见面试

  • 谈一下AQS吧 @可以从定义入手,然后讲
    • 不同锁状态的更改的实现方式
    • FIFO队列的实现方式
    • 核心技术CAS+volatile
  • CAS是什么?见上面的笔记
  • 为什么你说AQS的底层是CAS+volatile?
    • 表示锁状态的变量state,以及FIFO队列的头,尾,节点的状态都是volatile修饰的
    • 在设置state,队列的头,尾,状态的时候都有用到CAS技术
  • JUC包里的同步组件主要实现了AQS的哪些主要方法 ?
    • tryAcquire, tryRelease
    • tryAcquireShared, tryReleaseShared
    • isHeldExclusively
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值