题目:synchronized 锁的是什么?
难度:low(题库标注)
标签:java/lock, difficulty/low
评分:⭐⭐⭐⭐☆(4/5)
synchronized 的锁是加在对象头上的,锁的其实是那个对象,判定这个对象是否加了锁,是否有线程持有,对应的线程是不是当前线程,是在这对象头上做的判定。
Synchronized 的加锁的位置是可以在方法上,可以方法块的对象上。加在方法上,其实它是对当前这个类的对象加锁,加的是整体的一个锁。然后方法区内,它是因为你方法区里边,代码块里边,你会设置一个对象去加锁,它把那个锁就加在这个你设置的静态类的对象上。在对象头上去判定你这个锁。
优点:
不足:
this;静态方法锁的是 Class 对象(如 MyClass.class),不是"类",是一个具体的 Class 实例补充(必背):
synchronized 锁三种对象的对照表:
| 用法 | 锁的对象 | 典型场景 |
|---|---|---|
synchronized 非静态方法 | this | 单实例级别的互斥 |
synchronized 静态方法 | Class<T>.class 对象 | 类级别的互斥(所有实例共享一把锁) |
synchronized(obj) {} | 显式指定的 obj | 细粒度锁(常用一个 private final Object 作为锁) |
底层实现要点:
monitorenter / monitorexit面试陷阱:
synchronized("foo") {} 全 JVM 共享一把锁(常量池),大坑this 时如果用两个不同实例,锁互不干扰;但如果用 static synchronized,即使两个实例也共享同一把锁核心考点定位:区分 synchronized 三种用法各自锁定的对象,以及底层 monitor 机制,考察对 Java 内置锁粒度的理解。
知识点拆解:
monitorenter / monitorexit 指令加解锁this(实例级)、Class(类级单例)、显式对象(自定义细粒度);粒度过粗影响并发度,过细影响正确性private final Object lock = new Object() 作为专用锁对象,避免锁共享导致意外的互斥考察方向:
ReentrantLock 显式锁的粒度对比延伸递进面试题:
MyClass.class 就是那个对象引用;锁的是这个对象,天然全局共享synchronized("foo") 锁的是常量池里的 String,全 JVM 所有相同字面量的地方共享一把锁,容易造成全局串行化this 带来的隐患?this 会暴露给外部代码,外部可能拿到同一个对象做意外的同步;推荐私有 final 锁对象,只暴露必要的公共方法面试官您好,synchronized 本质是给一个对象加内置锁(monitor),锁信息存在对象头里,通过 monitorenter 和 monitorexit 字节码指令实现。它有三种典型用法,锁的对象各不相同:
第一种,synchronized 修饰非静态方法,锁的是 this——当前实例对象,只有同一实例的多个线程才会互斥。
第二种,synchronized 修饰静态方法,锁的是类的 Class 对象,比如 MyClass.class。每个类在 JVM 里加载后都有一个唯一的 Class 实例,所以 static synchronized 相当于类级别的锁,所有实例共享一把锁。
第三种,synchronized(obj) {} 代码块,锁的是显式传入的 obj。工程实践中推荐用一个私有的 final Object lock = new Object() 作为专用锁,粒度可控、不会被外部拿到。
有几个坑需要注意:第一,锁 this 时如果外部代码能拿到这个对象,会形成意外的共享锁;第二,synchronized("常量") 锁的是常量池 String,全 JVM 共享一把锁,大坑;第三,static synchronized 因为锁的是 Class 对象,两个不同的实例对象也会互相阻塞——这是很多人忽略的。