MST

星途 面试题库

面试题:Java并发编程中锁优化及AQS原理

在高并发场景下,锁的性能至关重要。请深入阐述Java中锁优化的技术,如偏向锁、轻量级锁、自旋锁等,说明它们的适用场景及工作原理。此外,简述AQS(AbstractQueuedSynchronizer)框架的设计思想、核心组件及其在实现锁和同步器中的作用。
41.6万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

Java中锁优化技术

  1. 偏向锁
    • 适用场景:适用于只有一个线程频繁访问同步块的场景。在这种场景下,偏向锁可以减少不必要的锁竞争,提高性能。
    • 工作原理:当一个线程访问同步块并获取锁时,会在对象头中记录当前线程的ID。之后该线程再次访问同步块时,只需检查对象头中记录的线程ID是否与自己一致,若一致则无需进行CAS操作来获取锁,从而节省开销。若有其他线程尝试获取锁,则偏向锁撤销,升级为轻量级锁。
  2. 轻量级锁
    • 适用场景:适用于多个线程在短期内交替访问同步块的场景。
    • 工作原理:当线程进入同步块时,如果同步对象没有被锁定(锁标志位为01),虚拟机首先在当前线程的栈帧中创建一个锁记录空间,用于存储锁对象目前的Mark Word的拷贝(官方称为Displaced Mark Word)。然后,线程尝试使用CAS操作将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁;如果失败,表示其他线程竞争锁,轻量级锁膨胀为重量级锁。
  3. 自旋锁
    • 适用场景:适用于线程持有锁的时间较短,且CPU资源不紧张的场景。
    • 工作原理:当一个线程尝试获取锁时,如果锁已经被其他线程持有,该线程不会立即阻塞,而是在原地空转若干次(自旋),期望在这段时间内持有锁的线程能够释放锁,从而避免线程上下文切换带来的开销。自旋次数通常由JVM参数控制,若自旋一定次数后仍未获取到锁,则该线程进入阻塞状态。

AQS框架

  1. 设计思想:AQS框架是基于FIFO队列的同步器框架,采用模板方法模式。它通过一个FIFO队列来管理等待获取同步状态的线程,同时利用一个int类型的变量来表示同步状态。子类通过继承AQS并实现其抽象方法来管理同步状态,从而实现不同类型的同步器。
  2. 核心组件
    • 同步队列:即FIFO队列,用于存储等待获取同步状态的线程。每个节点包含前驱节点、后继节点和线程引用。
    • 同步状态(state):一个int类型的变量,用于表示同步状态。不同的同步器对state的含义和操作方式不同,例如ReentrantLock中,state表示锁的持有次数。
  3. 在实现锁和同步器中的作用
    • 实现锁:以ReentrantLock为例,它内部的Sync类继承自AQS。获取锁时,调用AQS的acquire方法,该方法会尝试获取同步状态(tryAcquire方法,由子类实现),若获取失败则将当前线程封装成节点加入同步队列等待。释放锁时,调用AQS的release方法,尝试释放同步状态(tryRelease方法,由子类实现),若释放成功则唤醒同步队列中的头节点线程。
    • 实现同步器:如CountDownLatch、Semaphore等同步器也是基于AQS实现。CountDownLatch通过AQS的state表示计数,线程调用await方法时,若state不为0则进入同步队列等待,调用countDown方法时,state减1,当state为0时唤醒所有等待线程。Semaphore通过AQS的state表示信号量数量,获取信号量时尝试减少state,释放信号量时增加state。