JUC(19)

69 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第19天,点击查看活动详情

JUC

9.1 AQS原理

  1. 概述 全称是 AbstractQueuedSynchronizer,是阻塞式锁和相关的同步器工具的框架 特点:

    • 用 state 属性来表示资源的状态(分独占模式和共享模式),子类需要定义如何维护这个状态,控制如何获取 锁和释放锁
      • getState - 获取 state 状态
      • setState - 设置 state 状态
      • compareAndSetState - cas 机制设置 state 状态
      • 独占模式是只有一个线程能够访问资源,而共享模式可以允许多个线程访问资源
    • 提供了基于 FIFO 的等待队列,类似于 Monitor 的 EntryList
    • 条件变量来实现等待、唤醒机制,支持多个条件变量,类似于 Monitor 的 WaitSet

    子类主要实现这样一些方法(默认抛出 UnsupportedOperationException)

    • tryAcquire
    • tryRelease
    • tryAcquireShared
    • tryReleaseShared
    • isHeldExclusively

获取锁的姿势

// 如果获取锁失败
if (!tryAcquire(arg)) {
 // 入队, 可以选择阻塞当前线程 park unpark
}

1. 非公平锁(NonfairSync )实现原理

NonfairSync 继承自 AQS

默认使用非公平锁

一下分析在下面的源码进行分析

再点进去十分恐怖,可以直接打开IDEA进行分析

没有竞争时

  1. CAS 尝试将 state 由 0 改为 1,结果失败
  2. 进入 tryAcquire 逻辑,这时 state 已经是1,结果仍然失败
  3. 接下来进入 addWaiter 逻辑,构造 Node 队列
  • 图中黄色三角表示该 Node 的 waitStatus 状态,其中 0 为默认正常状态,-1表示当前节点有职责唤醒后续节点
  • Node 的创建是懒惰的
  • 其中第一个 Node 称为 Dummy(哑元)或哨兵,用来占位,并不关联线程

当前线程进入 acquireQueued 逻辑

  1. acquireQueued 会在一个死循环中不断尝试获得锁,失败后进入 park 阻塞
  2. 如果自己是紧邻着 head(排第二位),那么再次 tryAcquire 尝试获取锁,当然这时 state 仍为 1,失败
  3. 进入 shouldParkAfterFailedAcquire 逻辑,将前驱 node,即 head 的 waitStatus 改为 -1,这次返回 false
  4. shouldParkAfterFailedAcquire 执行完毕回到 acquireQueued ,再次 tryAcquire 尝试获取锁,当然这时 state 仍为 1,失败
  5. 当再次进入 shouldParkAfterFailedAcquire 时,这时因为其前驱 node 的 waitStatus 已经是 -1,这次返回 true
  6. 进入 parkAndCheckInterrupt, Thread-1 park(灰色表示)