Zeros Tech Zeros Tech
首页
架构
大数据
数据库
  • 面试

    • Java面试
    • 大数据面试
    • 架构面试
语言
运维
关于
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

迹_Jason

全栈工程师
首页
架构
大数据
数据库
  • 面试

    • Java面试
    • 大数据面试
    • 架构面试
语言
运维
关于
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • java

    • 设计模式

    • 多线程
    • 设计模式
    • 性能测试
    • CompletableFuture
    • ConcurrentHashMap原理
    • HashMap
    • Java 应用 TIPS
    • Java 专业方法集
    • java_io
    • Java11新特性
    • JAVA并发编程CAS和AQS
    • java汇总
    • Java基础
    • synchronized
    • ThreadLocal
    • Mapstruct
    • LockSupport
    • ReentrantLock
    • CAS
  • javaScript

  • python

  • scala

  • node

  • lua

  • rust

  • shell

  • Gradle

  • 语言
  • java
迹_Jason
2021-12-17

AQS

Java并发之AQS源码分析(一) - 云+社区 - 腾讯云 (opens new window)

🤔方向点:

* 双端队列
* waitStatus
* 公平锁与非公平锁
* 共享与非共享
* 重入锁

# 获取锁流程

1. 调用 acquire() 方法,(arg参数可以是任意的,在ReentrantLock中就是上锁数)
2. 调用tryAcquire() 方法,如果返回 false,也就是获取锁失败,将Node(exclusive)信息添加到队列末端
3. 执行 acquireQueued 的逻辑,该逻辑主要是判断当前节点的前置节点是否是头节点,来尝试获取锁,如果获取锁成功,则当前节点就会成为新的头节点,调用parkAndCheckInterrupt方法挂起线程

# state 变量

State 是每次加锁的时候加1,大于0说明存在锁,释放锁的时候减1。

# 公平锁与非公平锁

在AQS中并没有实现公平与非公平锁,这个功能是在ReentrantLock实现。

# 共享与非共享

提供了独占锁和共享锁的接口,但tryAcquire、tryAcquireShared接口都没有提供实现。

# waitState 变量

1. CANCELLED(1):取消状态,如果当前线程的前置节点状态为 CANCELLED,则表明前置节点已经等待超时或者已经被中断了,这时需要将其从等待队列中删除。
2. SIGNAL(-1):等待触发状态,如果当前线程的前置节点状态为 SIGNAL,则表明当前线程需要阻塞。
3. CONDITION(-2):等待条件状态,表示当前节点在等待 condition,即在 condition 队列中。
4. PROPAGATE(-3):状态需要向后传播,表示 releaseShared 需要被传播给后续节点,仅在共享锁模式下使用。

Thread 字段用于存储当前线程对象

# enq()方法

如果队尾节点为空,则初始化队列,将头节点设置为空节点,头节点即表示当前正在运行的节点;如果队尾节点不为空,则继续采取 CAS 操作,将当前节点加入队尾,不成功则继续自旋,直到成功为止;

# acquireQueued(final Node node, int arg)

判断当前节点的 pred 节点是否为 head 节点,如果是,则尝试获取锁;2.获取锁失败后,进入挂起逻辑。 提醒一点:我们上面也说过,head 节点代表当前持有锁的线程,那么如果当前节点的 pred 节点是 head 节点,很可能此时 head 节点已经释放锁了,所以此时需要再次尝试获取锁。

# shouldParkAfterFailedAcquire(Node pred, Node node)

判断 pred 节点状态,如果为 SIGNAL 状态,则直接返回 true 执行挂起;2.删除状态为 CANCELLED 的节点;3.若 pred 节点状态为 0 或者 PROPAGATE,则将其设置为为 SIGNAL,再从 acquireQueued 方法自旋操作从新循环一次判断。

通俗来说就是:根据 pred 节点状态来判断当前节点是否可以挂起,如果该方法返回 false,那么挂起条件还没准备好,就会重新进入 acquireQueued(final Node node, int arg) 的自旋体,重新进行判断。如果返回 true,那就说明当前线程可以进行挂起操作了,那么就会继续执行挂起。

释放锁主要是将头节点的后继节点唤醒,如果后继节点不符合唤醒条件,则从队尾一直往前找,直到找到符合条件的节点为止。

AQS 中阻塞挂起线程是通过 LockSupport.park() [[LockSupport]] 实现。

编辑 (opens new window)
上次更新: 2021/12/17, 16:15:07
最近更新
01
权限
12-17
02
SpringGateway
12-17
03
Spock
12-17
更多文章>
Theme by Vdoing | Copyright © 2021-2021 迹_Jason | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×