多线程问!

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

相关推荐
vx-Biye_Design1 小时前
springboot安阳地区研学旅游服务小程序-计算机毕业设计源码12785
java·vue.js·windows·spring boot·tomcat·maven·mybatis
whaledown1 小时前
Kafka 与 Java 消息队列入门:用订单场景理解核心机制
java·kafka·消息队列·springboot
Moshow郑锴2 小时前
Ubuntu用SDKMAN轻松管理多个Java 版本
java·ubuntu·sdkman
阿昌喜欢吃黄桃2 小时前
RocketMq事务消息原理
java·中间件·消息队列·rocketmq·mq
CoderYanger2 小时前
A.每日一题:2095. 删除链表的中间节点
java·数据结构·程序人生·leetcode·链表·面试·职场和发展
摇滚侠2 小时前
MyBatis+Spring+SpringMVC SSM 整合 179-185
java·spring·mybatis
我不是FD2 小时前
OpenAI vs Anthropic API 对比:流式返回 + Adapt 适配层完整方案
java·人工智能·python
Peter(阿斯拉)2 小时前
[Android]_[中级]_[如何创建MVVM架构原型]
android·java·架构·mvvm·viewmodel
地瓜伯伯2 小时前
从MESI缓存一致性协议讲透synchronized的底层
java·spring boot·spring·spring cloud·微服务·springcloud