同步机制的本质是“互斥 + 内存可见性保障”,通过加锁清空本地缓存、解锁强制刷回主内存来保障可见性、原子性和有序性;synchronized锁对象分别是this、Class对象或指定obj;ReentrantLock需在finally中显式unlock;锁升级由Mark Word状态动态控制,含偏向锁、轻量级锁和重量级锁。

同步机制的本质是“互斥 + 内存可见性保障”
Java里同步不是单纯为了“让代码串行执行”,而是为了解决三个硬性问题:一个线程改了共享变量,另一个线程得立刻看到(可见性);一个“读-改-写”操作不能被中途打断(原子性);指令执行顺序不能被JVM或CPU乱序优化打乱(有序性)。synchronized 和 ReentrantLock 都是通过「加锁时清空本地缓存、解锁时强制刷回主内存」来同时搞定这三件事的——这才是它不可替代的核心。
synchronized 的锁对象到底是谁?别再记混了
很多人写错同步范围,根源在于没搞清锁对象。它不是“方法”或“代码块”本身,而是一个实实在在的 Java 对象:
- 修饰实例方法 → 锁是
this,即当前对象实例 - 修饰静态方法 → 锁是
MyClass.class,即类的 Class 对象 - 修饰同步块
synchronized(obj) { ... }→ 锁就是你传进去的obj,必须确保所有竞争线程用的是同一个obj
常见错误:synchronized(new Object()) —— 每次都新建对象,根本锁不住任何东西;synchronized(this) 在单例或静态上下文中误用,导致锁粒度失控。
ReentrantLock 为什么不能只靠 lock()?
ReentrantLock 是显式锁,不自动释放,这点和 synchronized 有本质区别。漏掉 unlock() 就等于永久占着锁,其他线程永远卡住。
立即学习“Java免费学习笔记(深入)”;
- 必须在
finally块中调用unlock(),哪怕中间抛异常也要释放 - 不要在
try外面先lock(),否则异常可能发生在加锁前,finally里unlock()会报IllegalMonitorStateException - 如果要用
tryLock(long, TimeUnit),记得检查返回值:if (!lock.tryLock(1, TimeUnit.SECONDS)) { /* 处理获取失败 */ }
ReentrantLock lock = new ReentrantLock();
try {
if (lock.tryLock(2, TimeUnit.SECONDS)) {
// 临界区操作
} else {
throw new RuntimeException("获取锁超时");
}
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}锁升级和对象头里的 Mark Word 是什么关系?
JVM 对 synchronized 做了大量优化,但这些对开发者是透明的。真正影响你判断的是:锁状态存在对象头的 Mark Word 里,它会随竞争程度动态变化:
- 无竞争时 → 偏向锁(只记录偏向线程ID,几乎零开销)
- 有轻度竞争 → 轻量级锁(用CAS替换Mark Word,线程自旋等待)
- 竞争激烈 → 重量级锁(挂起线程,进入操作系统 Mutex,开销最大)
所以,不要一看到 synchronized 就觉得“重”。但要注意:频繁的锁撤销(如大量线程争抢同一把偏向锁)反而触发额外开销;若确定是高竞争场景,可考虑用 -XX:-UseBiasedLocking 关掉偏向锁,避免拖慢启动。










