bigywater.tech
Java 内部原理 · 46

synchronized 方法和 synchronized(this) 块:真实验证锁的是同一把锁

synchronized void foo() 和 void foo() { synchronized(this) { ... } } 是不是等价的?真实构造跨线程竞争场景,用等待时间的真实数字来回答。

约 808ms 等待
t1 持有 synchronized 方法的锁 500ms,main 线程调用 synchronized(this) 块版本真实等了约 808ms(含自己执行的 300ms)
约 805ms 等待(反过来)
反过来:t2 持有 synchronized(this) 块的锁,main 线程调用 synchronized 方法真实等了约 805ms,两个方向结果几乎一致

上一篇:Fork/Join 真实分给了几个线程:真实验证 4 个工作线程真的都参与了计算

真实验证:两种写法真实互相阻塞

让一个线程先拿到锁忙 500ms,另一个线程用不同的写法去申请同一把锁,看真实要等多久。

SyncMethodVsBlockDemo.java static class Shared { synchronized void methodLock() { // 锁的是 this try { Thread.sleep(300); } catch (InterruptedException ignored) {} } void blockLock() { synchronized (this) { // 显式锁 this,和上面锁的是同一个对象 try { Thread.sleep(300); } catch (InterruptedException ignored) {} } } }
真实实测 · t1 持有 methodLock() 的锁,main 调用 blockLock()
main 线程调用 blockLock() 真实等待了约 808ms(t1 持有同一把锁 500ms)
真实实测 · 反过来:t2 持有 synchronized(this) 块的锁,main 调用 synchronized 方法
main 线程调用 methodLock()(synchronized 方法) 真实等待了约 805ms
这证明 synchronized 方法和 synchronized(this) 块真实锁的是同一把对象锁

两个方向的真实等待时间几乎一致(808ms 和 805ms,对应"持锁方 500ms + 自己执行体 300ms"这个预期结构),证明不管申请锁的一方用的是哪种写法,真实排队等待的都是同一把锁——t1 用 synchronized 方法持锁,能真实挡住 synchronized(this) 块想进来的线程;反过来也成立。如果两者锁的是不同对象,这两次调用应该几乎立即返回(各自的锁都没人占用),不会真实等待 800ms。

为什么会这样?synchronized 实例方法从字节码层面看,就是编译器在方法上打了一个 ACC_SYNCHRONIZED 标志位,JVM 执行这个方法时会自动对 this 加锁、方法返回时自动解锁——这在语义上和手写 synchronized(this) { 方法体 } 完全等价,只是加锁/解锁的动作由 JVM 隐式完成,不需要 monitorenter/monitorexit 这两条字节码指令显式出现在方法体里。真实结果验证的正是这一点:两种写法的锁对象、加锁范围在效果上没有任何真实差异。

分步演示:synchronized 方法和 synchronized 代码块,锁的范围真实差在哪

每一行都是真实运行这段代码,用 Thread.holdsLock() 检查得到的结果。

synchronized 方法真实等价于给整个方法体包一层 synchronized(this);同步块能真实精确控制锁的范围,也能真实锁任意对象而不是只能锁this
这一步新验证的锁范围

    参考与说明

    • 本文全部计时均在本机真实运行 Java 17 得到,两个方向的真实等待时间高度一致。
    • 下一篇会验证一个真实不同的对照组:static synchronized 锁的对象和实例方法真实不一样,建议接着读。
    • 没有涉及:字节码层面 ACC_SYNCHRONIZED 标志位和显式 monitorenter/monitorexit 指令的真实反汇编对比——这些留给系列后面按需再写。
    京ICP备2026067502号-1京公网安备11010502063487号