Java 锁详解:互斥锁、读写锁、自旋锁与 synchronized 锁优化
锁的分类视角按乐观/悲观:synchronized、ReentrantLock 属悲观;CAS 自旋属乐观。按共享性:读写锁读共享写独占。按可中断/超时:ReentrantLock 提供 tryLock、lockInterruptibly。 synchronized 优化偏向锁减少无竞争开销,轻量级锁用 CAS 抢 Mark Word,竞争激烈膨胀为重量级监视器。JDK15 起偏向锁默认关闭,了解历史即可。 ReentrantLock 对比需手动 unlock,支持公平锁、Condition 多队列。示例: 123Lock lock = new ReentrantLock();lock.lock();try { /* 临界区 */ } finally { lock.unlock(); } 读多写少优先考虑 ReadWriteLock 或 StampedLock(注意不可重入读写规则)。 课堂外的思考学习「Java 锁详解:互斥锁、读写锁、自旋锁与 synchronized 锁优化」时,我把自己放在线上值班的情境里:...
Java并发常见面试题总结(上)
高频起点题进程与线程:进程是资源分配单位,线程是调度单位。Java 线程由 JVM 映射到 OS 线程(平台线程),虚拟线程则是轻量用户态调度。 创建方式:继承 Thread、实现 Runnable、Callable+FutureTask,或线程池。推荐后两种,便于复用与治理。 sleep vs wait:sleep 不释放锁,属于 Thread;wait 释放 monitor,必须在同步块内调用,属于 Object。 自测回答框架描述 synchronized 锁升级(偏向→轻量→重量)时,先说明对象头 Mark Word,再讲竞争触发膨胀,避免背诵术语而无场景。 原创追问「为什么 double-checked locking 需要 volatile?」——半初始化对象对其他线程可见会导致 NPE,这题把 JMM 与单例模式绑在一起,是区分背诵与理解的分水岭。 课堂外的思考学习「Java并发常见面试题总结(上)」时,我把自己放在线上值班的情境里:如果告警与这一主题相关,我能否在较短时间内建立假设并用数据验证?这种提问方式逼迫我从「看过」变成「讲得清楚、做得到」,也避免笔记沦...
Java 常见并发容器总结
选型表(简版) 场景 候选 备注 高并发读写 Map ConcurrentHashMap JDK8+ 桶头 CAS + synchronized 协同 读多写少 List CopyOnWriteArrayList 写时复制,迭代弱一致 阻塞队列 ArrayBlockingQueue / LinkedBlockingQueue 有界 vs 可选无界 延迟任务 DelayQueue 依赖 Comparable/Delayed 误区HashMap 并发 put 可能死循环(JDK7)或丢数据;Collections.synchronizedXxx 只是方法级互斥,复合操作仍需外部同步。 练习实现一个简易 LRU 缓存:ConcurrentHashMap 存 entry,LinkedHashMap 维护顺序,或用 Caffeine 对照理解 eviction 策略。 课堂外的思考学习「Java 常见并发容器总结」时,我把自己放在线上值班的情境里:如果告警与这一主题相关,我能否在较短时间内建立假设并用数据验证?这种提问方式逼迫我从「看过」变...
CAS 详解
三要素CAS 需要内存位置 V、旧值 A、新值 B:当且仅当 V 当前等于 A 时,才原子地写成 B。它提供了乐观并发的基础,但不保证整体业务原子性——多次 CAS 之间仍可能被其他线程插入。 ABA 与解决思路栈顶从 A→B→A,单纯值比较会误判「未变化」。AtomicStampedReference 用版本戳标记每次变更,或像 ConcurrentLinkedQueue 那样用唯一节点对象规避。 手写伪代码123456void increment(AtomicInteger counter) { for (;;) { int prev = counter.get(); if (counter.compareAndSet(prev, prev + 1)) return; }} 失败重试是常态,高竞争下 CAS 也会成为热点,需要降级为锁或分段。 课堂外的思考学习「CAS 详解」时,我把自己放在线上值班的情境里:如果告警与这一主题相关,我能否在较短时间内建立假设并用数据验证?这种提问方式逼迫我从「看过」变成「讲得清楚、...
Atomic 原子类总结
家族分工java.util.concurrent.atomic 提供基于 CAS 的无锁计数与引用更新。AtomicInteger 适合低竞争计数;LongAdder 通过 Cell 分段累加,在高并发统计场景通常优于 AtomicLong。 使用注意 compareAndSet 失败需要自旋或退避策略; AtomicReference 可配合不可变对象做无锁栈/队列; 字段更新器(FieldUpdater)适合已有类不便改继承结构时。 小实验压测 8 线程各累加 100 万次,对比 AtomicLong 与 LongAdder 的耗时。通常会看到 LongAdder 在写多读少时更稳,但 sum() 不是严格原子快照,报表场景要留意。 课堂外的思考学习「Atomic 原子类总结」时,我把自己放在线上值班的情境里:如果告警与这一主题相关,我能否在较短时间内建立假设并用数据验证?这种提问方式逼迫我从「看过」变成「讲得清楚、做得到」,也避免笔记沦为标题摘抄。 与同事的讨论方式我会用白板画出状态机、内存布局或调用链,请同事挑错。若被追问「更高并发或更老 JDK 版本会怎...
AQS 详解
核心抽象AQS(AbstractQueuedSynchronizer)像一套「排队取号机」:state 表示剩余许可或重入次数,获取失败就把当前线程封装成 Node 挂到双向队列,前驱释放时再唤醒后继。ReentrantLock、Semaphore、CountDownLatch 都只是覆写 tryAcquire/tryRelease 的不同策略。 为什么用 CLH 变体纯自旋在竞争激烈时会烧 CPU;纯阻塞又增加上下文切换。AQS 先短暂自旋再 park,并在双向链表里维护 SIGNAL/CANCELLED 等状态,保证唤醒路径可控。 原创示例12345678910111213141516class OneShotGate { private final Sync sync = new Sync(); void open() { sync.release(1); } void awaitOpen() throws InterruptedException { sync.acquireSharedInter...
Java 并发编程专题:线程、锁、JMM、CAS、AQS、线程池与虚拟线程
学习地图并发编程不是「会写 synchronized」就够了。我按由浅入深的顺序给自己划了一条路线:先搞清线程生命周期与中断语义,再理解 JMM 带来的可见性与有序性问题;接着用 CAS 和原子类解决简单计数场景,遇到复杂互斥再落到 AQS 及其衍生锁;最后把线程池参数调优和 Java 21 虚拟线程纳入工程实践。 自检清单 能否解释「创建线程」与「启动线程」的区别? 能否画出 happens-before 在 volatile 写/读上的作用? 能否说明为什么线程池不建议用 Executors 静态工厂? 小练习用两个线程交替打印 A/B 各 10 次,分别尝试 synchronized、ReentrantLock 和 Semaphore,体会 API 差异与可读性权衡。 课堂外的思考学习「Java 并发编程专题:线程、锁、JMM、CAS、AQS、线程池与虚拟线程」时,我把自己放在线上值班的情境里:如果告警与这一主题相关,我能否在较短时间内建立假设并用数据验证?这种提问方式逼迫我从「看过」变成「讲得清楚、做得到」,也避免笔记沦为标题摘抄。 与同事的讨论...
CompletableFuture 详解
设计动机Future.get 会阻塞调用线程;CompletableFuture 把「完成后的动作」注册为回调,天然支持 thenApply、thenCompose 链式组合,也提供 allOf/anyOf 聚合。 关键 API 心智 supplyAsync / runAsync 选择线程池,不要默认 commonPool 跑阻塞 IO; exceptionally/handle 统一异常出口; orTimeout(Java 9+)给慢调用加护栏。 示例:并行拉取1234CompletableFuture<String> user = CompletableFuture.supplyAsync(() -> fetchUser());CompletableFuture<List<Order>> orders = user.thenCompose(u -> CompletableFuture.supplyAsync(() -> fetchOrders(u.id())));String su...
Maven 核心概念总结
围绕“Maven 核心概念总结”,本文不按原目录复述,而是把内容整理为问题、机制、实践和取舍四层。阅读时可从“Maven 介绍、Maven 坐标、Maven 依赖、依赖配置”几个切面建立自己的判断框架。 核心认识开发工具的价值是让构建、运行和协作可重复。理解状态、依赖、版本与执行阶段,比记忆命令更重要;工具异常通常也应沿这些边界排查。 Maven 用坐标定位构件,以生命周期组织插件目标。传递依赖可能冲突,应查看依赖树,用 dependencyManagement 管版本,并在排除或覆盖后运行完整测试。 学习这类主题时,应先写清对象、边界和衡量指标,再讨论具体组件或技巧。同一方案放在不同流量、数据规模与团队能力下,结论可能相反;设计说明必须保留前提。 工程实践团队应固定工具入口与关键版本,让本地和 CI 执行同一流程。配置变更要能通过日志、依赖图或产物校验验证,并保留安全的回退方式。 实施建议采用小步验证:记录变更前基线,一次只调整少量关键变量,通过日志、指标和测试判断收益。上线后继续观察长尾与异常路径,并把操作步骤沉淀成可执行预案。 权衡与边界自动化提高效率,也可能隐藏隐式行...
Maven 最佳实践
围绕“Maven 最佳实践”,本文不按原目录复述,而是把内容整理为问题、机制、实践和取舍四层。阅读时可从“Maven 标准目录结构、明确指定 Java 编译版本、有效管理依赖关系、针对不同环境使用配置文件”几个切面建立自己的判断框架。 核心认识开发工具的价值是让构建、运行和协作可重复。理解状态、依赖、版本与执行阶段,比记忆命令更重要;工具异常通常也应沿这些边界排查。 Maven 项目应遵循标准目录,集中管理依赖与插件版本,并通过 Wrapper 保证入口一致。Profile 不宜制造完全不同的构建,父 POM 也不应让所有模块被动继承无关配置。 学习这类主题时,应先写清对象、边界和衡量指标,再讨论具体组件或技巧。同一方案放在不同流量、数据规模与团队能力下,结论可能相反;设计说明必须保留前提。 工程实践团队应固定工具入口与关键版本,让本地和 CI 执行同一流程。配置变更要能通过日志、依赖图或产物校验验证,并保留安全的回退方式。 实施建议采用小步验证:记录变更前基线,一次只调整少量关键变量,通过日志、指标和测试判断收益。上线后继续观察长尾与异常路径,并把操作步骤沉淀成可执行预案。...