🍂 枫言枫语:我是予枫,一名行走在 Java 后端与多模态 AI 交叉路口的研二学生。 “予一人以深耕,观万木之成枫。” 在这里,我记录从底层源码到算法前沿的每一次思考。希望能与你一起,在逻辑的丛林中寻找技术的微光。
在多线程的江湖里,“锁”永远是绕不开的核心话题。如果把计算机资源比作一份精美的外卖,那么线程就是饥肠辘辘的食客。如果没有合理的锁机制,这份外卖最终可能会变成一地狼藉。
今天,我们就来扒一扒 Java 中那些名目繁多的锁,看看它们是如何在方寸之间调度千军万马的。
在 Java 锁的世界观里,首先要分清的是对待竞争的态度。
性格: 极其谨慎,总觉得“总有刁民想害朕”。
逻辑: 每次去拿数据的时候,都认为别人会修改。所以不管三七二十一,先上锁,等自己用完了再解锁。
代表: synchronized 关键字、ReentrantLock。
场景: 写操作频繁、竞争激烈的场景。
性格: 阳光开朗,认为“大家都是文明人”。
逻辑: 拿数据时不上锁,但在更新时会检查一下:在我拿走后的这段时间,有人动过它吗?如果没有,就更新;如果有,就重试(自旋)。
实现: CAS (Compare and Swap) 机制。
代表: AtomicInteger、LongAdder。
场景: 读操作频繁、竞争较少的场景。
逻辑: 严格遵守“排队准则”。先申请锁的线程先进入队列,锁释放后,队列头的线程优先获得锁。
优点: 不会产生“线程饥饿”。
缺点: 吞吐量低,因为唤醒排队线程的开销很大。
逻辑: 允许“插队”。新来的线程会先尝试直接抢锁,抢不到再进队列。
优点: 性能极高。如果新来的线程恰好能利用 CPU 时间片抢到锁,就省去了线程切换的上下文开销。
注意: Java 中的 synchronized 是非公平的,ReentrantLock 默认也是非公平的。
定义: 又名“递归锁”。如果一个线程已经持有了一把锁,它可以再次进入同一个锁保护的代码块,而不会被自己锁死。
形象比喻: 你拿着自家大门的钥匙进了屋,进卧室门的时候不需要再重新去物业办一把钥匙。
代表: synchronized 和 ReentrantLock 都是可重入的。
逻辑: 霸道总裁,锁只能被一个线程持有。无论是读还是写,只要我在这里,别人都得等着。
代表: synchronized。
逻辑: 暖男属性。支持多个线程同时持有锁,通常用于“只读”操作。
代表: ReentrantReadWriteLock 中的 读锁。
ReadWriteLock 策略:
为了平衡性能与安全,HotSpot 虚拟机对 synchronized 进行了史诗级优化,让它不再是一上来就动用重量级武器。
写出高性能并发代码的秘诀不在于“精通各种锁”,而在于**“尽量少用锁,用了也要快”**:
Atomic 原子类。
ThreadLocal 让每个线程拥有自己的拷贝。
Java 的锁机制就像是一套复杂的城市交通指挥系统。悲观锁是红绿灯,乐观锁是互相礼让的环岛,而锁升级则是根据车流量自动切换的管理方案。
理解这些锁的本质,能让你在处理高并发需求时游刃有余。下次面试官再问你“Java 锁有哪些”,希望你能从容地告诉他:“这不仅仅是 API 的调用,更是对资源竞争的艺术处理。”
关于作者: 💡 予枫,某高校在读研究生,专注于 Java 后端开发与多模态情感计算。💬 欢迎点赞、收藏、评论,你的反馈是我持续输出的最大动力!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。