北京JAVA基础面试30天打卡03

1.AQS(AbstractQueuedSynchronizer)

AQS(AbstractQueuedSynchronizer) 是 Java 并发包(java.util.concurrent)中的一个核心框架类,用于构建锁和同步器(如 ReentrantLock、Semaphore、CountDownLatch 等)。它提供了一种基于队列的同步机制,管理线程的阻塞和唤醒。

核心思想

AQS 通过一个 FIFO 队列 (基于 CLH 锁队列的变种)和一个 状态变量(int state)来实现线程同步:

  • 状态变量:表示锁或同步器的状态(如锁是否被占用,计数器的值等)。
  • 队列:维护等待获取锁的线程,线程进入队列后会被阻塞,直到满足条件被唤醒。
  • CAS 操作:通过 Compare-And-Swap(如 compareAndSetState)实现状态的原子更新。
工作原理
  1. 状态管理:AQS 维护一个 volatile int state,通过 CAS 操作修改状态。例如,ReentrantLock 中 state=0 表示锁空闲,state>0 表示锁被占用。

  2. 队列管理:当线程无法获取锁时,加入 AQS 的等待队列(CLH 队列),线程会被挂起(LockSupport.park)。

  3. 获取/释放

    • 获取锁:子类实现 tryAcquire 方法,尝试更新 state。成功则获取锁,失败则进入队列。
    • 释放锁:子类实现 tryRelease 方法,更新 state,并唤醒队列中的下一个线程(LockSupport.unpark)。
  4. 条件队列:AQS 支持 Condition 接口(如 ConditionObject),用于实现线程的条件等待(如 await 和 signal)。

核心方法
  • 模板方法

    (由 AQS 提供,子类实现):

    • tryAcquire(int):尝试获取锁。
    • tryRelease(int):尝试释放锁。
    • tryAcquireShared(int):尝试共享式获取资源。
    • tryReleaseShared(int):尝试共享式释放资源。
    • isHeldExclusively():判断锁是否独占。
  • 核心 API

    • acquire(int):获取锁(独占模式),包括尝试获取和入队阻塞。
    • release(int):释放锁,唤醒队列中的线程。
    • acquireShared(int) / releaseShared(int):共享模式的获取和释放。
使用场景

AQS 是许多 JUC 工具类的底层实现:

  • ReentrantLock:基于 AQS 实现可重入锁,state 表示锁的重入次数。
  • ReentrantReadWriteLock:读写锁,state 高 16 位表示读锁计数,低 16 位表示写锁状态。
  • Semaphore:信号量,state 表示可用许可数。
  • CountDownLatch:倒计数器,state 表示剩余计数。
  • CyclicBarrier:state 用于管理屏障状态。
优点
  • 灵活性:通过模板方法,子类可以自定义锁的语义。
  • 高效性:基于 CAS 和队列,减少上下文切换。
  • 可扩展性:支持独占锁、共享锁、条件等待等。

2. Synchronized 与 ReentrantLock 的区别

SynchronizedReentrantLock 都是 Java 中用于实现线程同步的机制,但它们在功能、性能和使用方式上有显著区别。

Synchronized
  • 定义:Java 关键字,内置的锁机制,由 JVM 管理。

  • 特点

    • 使用简单:通过 synchronized 关键字修饰代码块或方法,自动获取和释放锁。
    • 独占锁:同一时间只有一个线程可以持有锁。
    • 可重入:同一线程可以多次获取同一锁(锁计数器递增)。
    • 不可中断:线程在等待锁时无法被中断。
    • 自动释放:锁在代码块结束或异常时自动释放。
    • 无条件等待:不支持 Condition 机制,无法实现复杂等待/通知逻辑。
    • 性能:在 Java 6 之后,JVM 对 synchronized 进行了优化(如锁消除、锁粗化、偏向锁、轻量级锁),性能接近 ReentrantLock。
ReentrantLock
  • 定义:基于 AQS 实现的显式锁,位于 java.util.concurrent.locks 包。

  • 特点

    • 显式锁:需要手动调用 lock() 和 unlock() 获取/释放锁。
    • 可重入:与 synchronized 类似,支持重入。
    • 可中断:支持 lockInterruptibly(),允许线程在等待锁时被中断。
    • 公平/非公平:支持公平锁(按等待顺序分配)和非公平锁(默认,允许插队)。
    • 条件等待:支持 Condition 对象,可实现多个等待队列(如生产者-消费者模型)。
    • 灵活性:支持超时获取锁(tryLock(timeout))。
    • 手动释放:需在 finally 块中调用 unlock(),否则可能导致死锁。
区别总结
特性 Synchronized ReentrantLock
实现方式 JVM 内置,关键字 JUC 包,基于 AQS
使用方式 自动获取/释放锁 手动 lock() / unlock()
可中断性 不可中断 可中断(lockInterruptibly)
公平性 非公平锁 可选公平锁或非公平锁
条件等待 无 Condition 支持 支持 Condition(多等待队列)
灵活性 简单但功能有限 功能丰富(超时、尝试锁等)
性能 Java 6 后优化,接近 ReentrantLock 稍高(尤其在复杂场景下)
异常处理 自动释放锁 需手动释放(finally 块)
使用场景
  • Synchronized:适合简单同步场景,如方法级或代码块级锁,代码简洁,适合快速开发。
  • ReentrantLock:适合需要高级功能的场景,如公平锁、可中断锁、条件等待、超时获取锁等。

代码示例

java

java 复制代码
// Synchronized

synchronized (obj) {
    // 同步代码块
}

// ReentrantLock
ReentrantLock lock = new ReentrantLock();

try {
    lock.lock();
    // 同步代码块
} finally {
    lock.unlock();
}

3. Java 中 volatile 关键字的作用

volatile 是 Java 中的一个关键字,用于修饰变量,确保变量在多线程环境下的可见性有序性 ,但不保证原子性

作用
  1. 保证可见性

    • 当一个线程修改了 volatile 变量的值,新值对其他线程立即可见。
    • 这是因为 volatile 变量的读写操作会直接与主内存交互,绕过线程的本地缓存(工作内存)。
    • 解决了多线程环境下缓存不一致的问题。
  2. 防止指令重排序

    • volatile 变量的读写操作会添加内存屏障(Memory Barrier),防止 JVM 或 CPU 进行指令重排序。
    • 确保代码的执行顺序与程序顺序一致(如初始化对象的正确性)。
  3. 不保证原子性

    • volatile 仅保证读写的原子性,但对复合操作(如 i++)不保证原子性。
    • 例如,volatile int i 的 i++ 操作分为读、改、写三步,多个线程可能交错执行,导致数据不一致。
使用场景
  • 状态标志:如 volatile boolean running = true,用于控制线程的启动/停止。
  • 单次写入,多次读取:如配置变量,初始化后只读不写。
  • 配合其他机制:与 synchronized 或 Atomic 类结合使用,解决原子性问题。
示例

java

java 复制代码
class VolatileExample {

    volatile boolean flag = false;
    void writer() {
        flag = true; // 写入对其他线程立即可见
    }
    void reader() {
        if (flag) { // 读取到最新值
            System.out.println("Flag is true");
        }
    }
}
volatile 与 synchronized 的区别
  • volatile
    • 仅保证变量的可见性和有序性,不保证复合操作的原子性。
    • 轻量级,适用于简单场景(如标志位)。
  • synchronized
    • 保证原子性、可见性和有序性。
    • 重量级,适用于需要锁保护的复杂同步场景。
注意事项
  • volatile 不适合需要原子性的场景(如计数器),应使用 AtomicInteger 或 synchronized。
  • volatile 的性能开销低于 synchronized,但仍需谨慎使用,避免滥用。

总结

  • AQS:Java 并发框架的核心,基于状态变量和队列实现锁和同步器,广泛用于 JUC 工具类。
  • Synchronized vs ReentrantLock:Synchronized 简单但功能有限,ReentrantLock 提供更多灵活性(如公平锁、条件等待)。
  • volatile:确保变量的可见性和有序性,适合状态标志等场景,但不保证原子性。
  • 明天二次补充拓展,希望大家坚持下去,不见彩虹也有阳光~
相关推荐
要记得喝水12 分钟前
汇编中常用寄存器介绍
开发语言·汇编·windows·c#·.net
shi578327 分钟前
C# 常用的线程同步方式
开发语言·后端·c#
凌晨7点35 分钟前
控制建模matlab练习11:伯德图
开发语言·matlab
sg_knight43 分钟前
Spring Cloud Gateway全栈实践:动态路由能力与WebFlux深度整合
java·spring boot·网关·spring·spring cloud·微服务·gateway
JosieBook1 小时前
【IDEA】IntelliJ IDEA 中文官方文档全面介绍与总结
java·ide·intellij-idea
三只蛋黄派1 小时前
Websocket
java
JIngJaneIL1 小时前
专利服务系统平台|个人专利服务系统|基于java和小程序的专利服务系统设计与实现(源码+数据库+文档)
java·数据库·小程序·论文·毕设·专利服务系统平台
崎岖Qiu1 小时前
leetcode1343:大小为K的子数组(定长滑动窗口)
java·算法·leetcode·力扣·滑动窗口
freed_Day1 小时前
Java学习进阶--集合体系结构
java·开发语言·学习
Shun_Tianyou2 小时前
Python Day25 进程与网络编程
开发语言·网络·数据结构·python·算法