Java并发编程中的AQS

作者:zhangyunlong 发布时间: 2024-09-07 阅读量:3 评论数:0
  • AQS起到了一个抽象, 封装的作用, 将一些排队, 入队, 加锁, 中断等方法提供出来, 便于其他相关juc锁的使用, 具体的加锁时机和入队时机需要实现类自己控制.

  • AQS通过维护一个共享状态(state)和一个FIFO的双向链表来管理线程对共享资源的访问.

  • state用volatile修饰, 表示当前资源的状态. 例如在独占锁中, state为0表示未占用, 为1表示已占用.

  • 当线程尝试获取资源失败时, 会被加入到AQS的等待队列中. 这个队列是一个变体的CLH队列, 采用双向链表结构, 节点包含线程的引用, 等待状态, 以及前后节点的指针.

  • AQS常见的实现类有ReentrantLock, CountDownLatch等.

ReentrantLock

  • ReentrantLock是一个基于AQS实现的可重入锁, 支持公平和非公平两种方式.

  • 内部实现依靠一个state变量和两个等待队列: 同步队列和等待队列.

  • 利用CAS修改state来竞争锁.

  • 竞争失败则进入同步队列(双向链表).

  • 条件condation不满足时进入等待队列(单项链表).

  • 是否是公平锁的区别在于: 线程获取锁时是加入到同步队列尾部还是直接利用CAS竞争锁.

image-CRdY.png

扩展

CAS

  • CAS是一种硬件级别的原子操作, 它比较内存中的某个值是否为预期值, 如果是则更新, 否则不修改.

  • 原理

    • 比较: CAS会检查内存中的某个值是否与预期值相等.

    • 交换: 如果相等, 则修改为新值.

    • 失败重试: 如果不相等, 说明有其他线程修改了这个值, CAS操作失败, 一般会利用重试, 直到成功.

  • 优点

    • 无锁并发: 不会导致线程阻塞, 提高并发性能.

    • 原子性: CAS操作是原子的, 线程安全.

  • 缺点

    • ABA问题: CAS操作中, 一个值从A变为B再变为A, CAS无法检测到这种变化, 可能导致错误. 可通过JDK的Atomic解决.

    • 自旋开销: CAS操作通过自旋实现, 可能导致CPU资源浪费.

    • 单变量限制: 不实用涉及多个变量的复杂操作.

自旋锁

  • 自旋锁是一种轻量级的锁机制, 线程在获取锁失败后不会立即进入阻塞状态, 而是在循环中反复尝试获取锁, 直至成功.

  • 这种方式避免了线程的上下文切换开销, 所以成为轻量级锁, 适合等待时间较短的场景.

  • 缺点

    • 并发情况下, 可能存在某个线程一直CAS失败, 获取不到锁.

    • 多核CPU在对同一变量进行并发CAS时, 会导致总线风暴.

CLH

  • 针对自旋锁的问题, 演进出的一种基于队列的自旋锁, 它适用于多核CPU环境下的并发场景.

  • 原理

    • 通过维护一个隐式队列, 使线程在等待锁时自旋在本地变量上, 减少对共享变量的竞争.

    • 它将竞争的线程组成一个队列, 通过排队的方式按序争抢锁.

    • 且每个线程不再CAS竞争一个变量, 而是自旋判断排序在自己前面的线程的状态, 如果前面的线程释放锁, 那么后续的线程则竞争锁.

image-abYv.png

  • 缺点

    • 占用CPU资源: 自旋期间一直占用CPU资源, 适合等待时间较短的场景.

评论