匕首指令免安装中文正式版
2.55G · 2025-10-11
原文来自于:zha-ge.cn/java/95
有时候你写 Java,总觉得自己像个江湖上的镖师,左挡右护,不就想保证点安全么?可Java的并发世界水深火热,总掏点“锁”出来——尤其那只老生常谈的synchronized
。今天就聊聊,它到底凭什么让多线程乖乖听话:啥原子性、可见性、有序性,这把锁都给咱整明白了没?
说起来,初入江湖我也天真。以为synchronized
真的只是个语法糖,平平无奇嘛,synchronized(this) { ... }
,进来转一圈就出去了,最多慢点罢了。但有一天,师傅咂吧咂吧嘴:“你知道这玩意到底怎么保安全的吗?光靠编译器?Dream on。”
一语点醒梦中人:synchronized
既不是魔法,也不是纯忽悠。它是怎么“定海神针”似地,把线程按住的?
乱翻资料时,冷不丁瞄到JVM里啥叫“对象头”(Object Header),再结合synchronized
用法,谜团渐渐解锁:
对象头里有Mark Word 别小瞧这玩意,线程拿锁全靠它做标记。什么轻量锁、偏向锁、重量锁,翻来覆去全在你对象头里搅和。
Monitor,实为底层功臣 JVM分配的Monitor对象,负责排队、唤醒、混合通知,像个勤快的门卫。
内存屏障
这才是synchronized
保障三大特性的底牌!每次加锁解锁,JVM都会在字节码里加上monitorenter/monitorexit指令,插入内存屏障,刷一遍主内存。
上一篇代码吐槽:
synchronized(obj) {
// 可能同时有10个线程要用这个资源
doSomething(); // 这里安全了
}
你以为这就是if(有人进来) { 等一等 }
这么简单吗?No no no,实际 JVM 代码执行 roughly 是:
说个真实故事。某天我自信给counter++加上synchronized
,性能稳得一批。
private int counter = 0;
public void add() {
synchronized(this) {
counter++;
}
}
问题来了——老板疯狂问我为什么吞掉了“可见性问题”。什么鬼?代码里不是锁得死死的?
其实是我误会了锁的作用域——要命的时候发现,锁的是堆上的对象,对象头一切OK,但如果你给别的对象加锁,只保证持有锁的那段代码安全,可视野之外满是危险。
而且,可见性靠的不是你开的锁,而是加解锁那一瞬间的【内存屏障】。少了它,线程A看不见线程B刚写入的值,数据像“隔壁老王”一样神秘莫测。还有队友脑洞太大,直接用synchronized(new Object())
,结果每次锁对象都不一样——等于没加锁,简直字节码水平的社会性死亡!
这几年下来,和synchronized
斗智斗勇,总结几点超实用经验,闪电给你划重点:
最后,面试遇到“三大特性”,别只会嘴巴说“原子性、可见性、有序性好呀”——背后原理、对象头里藏的秘密,你得有点小故事,才敢拍胸脯说“我懂点门道”!
搞半天,synchronized
其实江湖地位还挺有趣——它介于玄学和底层之间。谁说锁一定烂,关键看你会不会用,用得巧,JVM也会帮你省事。好了我去喝杯咖啡,写bug得趁热。
滴滴自动驾驶宣布获得 20 亿 D 轮融资,加大 AI 研发、推动 L4 落地
迈从推出 G75 V2 机械键盘:三模 10000mAh 大电池,配竖向旋钮