外观
synchronized 修饰在类的静态方法上和一个实例方法上有什么区别
⭐ 题目日期:
美团 - 2025/4/4,2024/12/23
📝 题解:
在Java中,synchronized
修饰静态方法和实例方法时,锁的作用范围和锁对象不同,具体区别如下:
1. 锁的作用范围
方法类型 | 锁对象 | 锁的作用范围 |
---|---|---|
静态方法 | 类的 Class 对象 | 所有调用该静态方法的线程(跨实例) |
实例方法 | 当前实例(this ) | 仅当前实例的同步方法 |
2. 并发行为示例
(1) 静态方法锁(类锁)
public class MyClass {
// 静态同步方法
public static synchronized void staticSyncMethod() {
// 代码块
}
}
- 锁对象:
MyClass.class
。 - 并发表现:
- 所有线程(无论操作哪个实例)调用
staticSyncMethod()
时,会竞争同一把锁。 - 例如,线程A调用
MyClass.staticSyncMethod()
,线程B调用MyClass.staticSyncMethod()
,线程B会被阻塞。
- 所有线程(无论操作哪个实例)调用
(2) 实例方法锁(对象锁)
public class MyClass {
// 实例同步方法
public synchronized void instanceSyncMethod() {
// 代码块
}
}
- 锁对象:当前实例(
this
)。 - 并发表现:
- 不同实例的
instanceSyncMethod()
互不影响。 - 同一实例的多个同步方法会共享锁。
- 例如,线程A调用
obj1.instanceSyncMethod()
,线程B调用obj2.instanceSyncMethod()
,两个线程不会阻塞;但线程B若调用obj1.instanceSyncMethod()
,则会被阻塞。
- 不同实例的
3. 锁的隔离性
- 静态方法锁 vs 实例方法锁:
- 静态方法锁(类锁)和实例方法锁(对象锁)互不干扰,因为它们的锁对象不同。
- 示例:
public class MyClass { public static synchronized void staticSync() {} // 锁:MyClass.class public synchronized void instanceSync() {} // 锁:this }
- 线程A调用
MyClass.staticSync()
,线程B调用obj.instanceSync()
,两者不会阻塞。
- 线程A调用
4. 代码验证
(1) 静态方法锁阻塞测试
public class LockTest {
public static synchronized void staticLock() {
System.out.println("静态方法加锁");
try { Thread.sleep(3000); } catch (Exception e) {}
}
public static void main(String[] args) {
new Thread(() -> LockTest.staticLock()).start();
new Thread(() -> LockTest.staticLock()).start(); // 第二个线程会等待
}
}
输出:
静态方法加锁
(3秒后)
静态方法加锁
(2) 实例方法锁不阻塞测试
public class LockTest {
public synchronized void instanceLock() {
System.out.println("实例方法加锁");
try { Thread.sleep(3000); } catch (Exception e) {}
}
public static void main(String[] args) {
LockTest obj1 = new LockTest();
LockTest obj2 = new LockTest();
new Thread(() -> obj1.instanceLock()).start();
new Thread(() -> obj2.instanceLock()).start(); // 第二个线程直接执行
}
}
输出:
实例方法加锁
实例方法加锁
(无等待)
5. 关键总结
维度 | 静态方法锁 | 实例方法锁 |
---|---|---|
锁对象 | 类的 Class 对象 | 当前实例(this ) |
作用范围 | 全局(跨实例) | 仅当前实例 |
适用场景 | 保护静态变量或类级别的共享资源 | 保护实例变量或对象级别的共享资源 |
并发隔离性 | 不同实例的线程会互斥 | 不同实例的线程不会互斥 |
6. 注意事项
避免锁混淆:
若在静态方法中同步代码块使用Class
锁,或在实例方法中同步代码块使用this
锁,需注意锁对象是否合理。// 静态方法中显式使用实例锁(错误!) public static void method() { synchronized (new MyClass()) { // 无意义,每次锁不同对象 // ... } }
性能影响:
- 静态方法锁(类锁)范围大,可能成为性能瓶颈。
- 实例方法锁粒度更细,适合高并发场景。
结论
- 静态方法锁:全局互斥,保护类级别的共享资源。
- 实例方法锁:实例级互斥,保护对象级别的共享资源。
- 设计建议:根据资源的作用域选择合适的锁,避免过度同步导致性能下降。