多线程问!

2.4 CAS 知道吗?

CAS 就是 Compare And Swap,比较并交换,它体现的是乐观锁思想。

它会比较当前内存值和期望值,如果相等,就更新成新值;如果不相等,说明被其他线程改过,就更新失败,通常会自旋重试。

Java 里的 Atomic 类和 AQS 都大量用了 CAS。它底层依赖 CPU 原子指令保证比较和更新的原子性。CAS 的优点是低竞争下避免加锁阻塞,但缺点是高竞争会自旋浪费 CPU,也有 ABA 问题。

2.5 请谈谈你对 volatile 的理解

volatile 我理解它主要解决两个问题:一个是可见性,一个是禁止指令重排序。

可见性就是说,一个线程修改了 volatile 变量之后,其他线程后续读取这个变量时,能够读到最新值,不会一直读自己线程里的旧缓存。禁止重排序主要是通过内存屏障实现的,保证 volatile 读写前后的指令不会被随意调整,比如双重检查锁里就需要 volatile 防止对象还没初始化完就被其他线程拿到。

但 volatile 有个重要限制,它不保证复合操作的原子性。比如 volatile int count,执行 count++ 还是线程不安全,因为 ++ 包含读取、加一、写回三步,多个线程并发时还是会丢失更新。所以 volatile 更适合做状态标记,或者配合一些特定场景使用,不能替代 synchronized 或 Atomic 类。

2.6 什么是AQS?

AQS 是 AbstractQueuedSynchronizer,可以理解成 JUC 里面很多锁和同步器的基础框架,比如 ReentrantLock、Semaphore、CountDownLatch 底层都跟它有关。

它的核心主要是两部分:一个是 state,一个是 FIFO 等待队列。state 是一个同步状态,具体含义由不同工具自己定义。比如 ReentrantLock (阻塞式锁)里,state 表示锁的重入次数;Semaphore 里表示剩余许可数量;CountDownLatch (倒计时锁)里表示倒计数。修改 state 时通常会用 CAS 来保证线程安全。

大概流程是:线程先尝试获取 state,如果获取成功就继续执行;如果失败,就会被封装成节点放到 AQS 的等待队列里阻塞。等持有资源的线程释放之后,再唤醒队列里的后继线程继续竞争。

所以我理解 AQS 的核心就是:用 state 管资源状态,用 CAS 改状态,用 FIFO 队列管理等待线程。

2.7 ReentrantLock的实现原理

ReentrantLock 是一个可重入互斥锁,使用上是通过 lock() 获取锁,unlock() 释放锁。它底层主要是基于 AQS 实现的。

在 ReentrantLock 里,AQS 的 state 表示锁的重入次数。state=0 表示锁还没人持有,线程加锁时会通过 CAS 尝试把 state 从 0 改成 1,成功就说明拿到了锁,同时会记录当前持锁线程。如果同一个线程再次调用 lock(),不会阻塞,而是直接让 state++,这就是可重入。

如果其他线程来抢锁失败,就会被封装成节点放进 AQS 的 FIFO 等待队列里阻塞。释放锁时,每次 unlock() 会让 state--,只有减到 0 才算真正释放锁,然后会唤醒队列里的后继线程继续竞争。

它还支持公平锁和非公平锁,默认是非公平锁。非公平锁线程来了会先尝试直接抢锁,可能插队;公平锁会先看队列里有没有线程在等,有的话就排队,所以公平锁更公平,但性能通常差一点。

2.8 synchronized和Lock有什么区别 ?

synchronized 和 Lock 都可以实现互斥和可重入,但它们最大的区别是使用方式和功能灵活性不一样。

synchronized 是 Java 关键字,由 JVM 管理锁的获取和释放。进入同步代码块时自动加锁,退出代码块或者发生异常时也会自动释放,所以用起来比较简单,不容易忘记释放锁。

Lock 是 JUC 里的接口,常见实现是 ReentrantLock。它需要手动调用 lock()unlock(),所以一般要把 unlock() 放到 finally 里,避免异常导致锁不释放。它的优势是功能更灵活,比如可以尝试获取锁 tryLock(),可以超时等待,可以响应中断,也可以选择公平锁,还支持多个 Condition 条件队列。

性能上现在不能简单说谁一定更快。synchronized 经过 JVM 优化后性能已经不差。一般简单同步场景我会优先用 synchronized;如果需要可中断、超时、公平锁或者多个条件队列,就用 ReentrantLock 这类 Lock 实现。

相关推荐
萧瑟余晖2 小时前
Java深入解析篇五十五之Unsafe 机制详解
java
kakawzw2 小时前
mybatis源码笔记1——JDBC和整体架构
java·mybatis
sunshine22 girl2 小时前
Java学习一 环境配置1 安装JDK,配置环境变量
java·开发语言·学习
秋饼2 小时前
Spring AI Session API 深度实战:从 ChatMemory 平滑迁移到事件溯源的企业级短期记忆
java·ai·技术分享·后端开发
QCodingDev2 小时前
Spring AI 2.0企业级RAG实战:引用校验、无依据拒答与知识治理怎么做?
java·人工智能·spring·ai
吴声子夜歌2 小时前
ApacheCommons——commons-math3(科学计算与线性代数)(一)
java·线性代数·算法·apache
砚底藏山河2 小时前
行情工程实战 M01|存储选型 CSVSQLiteMySQL
java·python·金融·maven
名字还没想好☜2 小时前
Java 用 LinkedHashMap 三行实现 LRU 缓存:accessOrder、removeEldestEntry 与线程安全
java·后端·安全·spring·缓存
落魄大学生之流水线上谋生计2 小时前
JVM程序计数器详解
jvm
谢亮_vipxieliang2 小时前
ValidX vs Google Guava Preconditions:验证 vs 断言
java·spring boot·后端·spring cloud·hibernate·guava