多线程问!

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 实现。

相关推荐
Ivanqhz9 小时前
Rust &‘static str浅析
java·前端·javascript·rust
weixin_4196583112 小时前
Docker 搭建 Jenkins 服务
java·docker·jenkins
captain37612 小时前
多线程线程安全问题
java·java-ee
CoderYanger13 小时前
A.每日一题:1979. 找出数组的最大公约数
java·程序人生·算法·leetcode·面试·职场和发展·学习方法
灵极海13 小时前
LangChain4j RAG 实战完整指南:从入门到踩坑
java·langchain
yaoxin52112313 小时前
476. Java 反射 - 调用方法
java·开发语言
wear工程师14 小时前
WebFlux 里能不能直接 block?面试别只答「不能」
spring
Ivanqhz14 小时前
Rust Lazy浅析
java·javascript·rust
bksczm14 小时前
Linux之日志和线程池、内存池
java·开发语言
笨蛋不要掉眼泪14 小时前
Java虚拟机:对象复活、引用强度与Stop-The-World
java·开发语言·jvm