synchronized锁普通方法和锁静态方法有什么区别?
Git高速下载
程序员面试资料大全|各种技术书籍等资料-1000G
IDEA开发工具- FREE
核心区别对比
特性 | 普通方法 (synchronized 方法) | 静态方法 (static synchronized 方法) |
---|---|---|
锁对象 | 当前实例对象 (this ) | 类的 Class 对象 (如 MyClass.class ) |
作用范围 | 同一实例的多个线程之间同步 | 所有实例的线程之间同步(跨实例全局锁) |
并发行为 | 不同实例的线程可以并发执行 | 所有实例的线程互斥执行 |
内存位置 | 堆内存(对象头) | 方法区(类元数据) |
适用场景 | 保护实例变量 | 保护类静态变量 |
详细解析
1. 锁对象不同
普通方法:
public synchronized void normalMethod() {// 锁对象 = this (当前实例)
}
- 等价于:
public void normalMethod() {synchronized(this) {// ...} }
静态方法:
public static synchronized void staticMethod() {// 锁对象 = MyClass.class
}
- 等价于:
public static void staticMethod() {synchronized(MyClass.class) {// ...} }
2. 作用范围不同
场景演示
class Counter {// 普通同步方法public synchronized void add(int value) {// ...}// 静态同步方法public static synchronized void staticAdd(int value) {// ...}
}
执行情况:
-
同一实例:
Counter c1 = new Counter(); // 线程1和线程2竞争同一把锁(c1实例锁) new Thread(() -> c1.add(1)).start(); new Thread(() -> c1.add(2)).start(); // 互斥执行
-
不同实例:
Counter c1 = new Counter(); Counter c2 = new Counter();// 普通方法:不互斥(不同锁对象) new Thread(() -> c1.add(1)).start(); // 锁 c1 new Thread(() -> c2.add(2)).start(); // 锁 c2 → 并发执行// 静态方法:互斥(同一Class锁) new Thread(() -> Counter.staticAdd(1)).start(); // 锁 Counter.class new Thread(() -> Counter.staticAdd(2)).start(); // 锁 Counter.class → 互斥执行
3. 并发行为差异
操作组合 | 是否互斥 |
---|---|
线程A:obj1.normalMethod() | |
线程B:obj1.normalMethod() | ✔️ 互斥 |
线程C:obj2.normalMethod() | ✘ 并发 |
线程D:MyClass.staticMethod() | |
线程E:MyClass.staticMethod() | ✔️ 互斥 |
线程F:obj1.staticMethod() | ✔️ 互斥 |
注意:
obj1.staticMethod()
与MyClass.staticMethod()
使用相同的 Class 锁
4. 实际应用场景
使用普通方法锁:
public class BankAccount {private double balance; // 实例变量public synchronized void deposit(double amount) {balance += amount; // 保护实例状态}
}
使用静态方法锁:
public class IdGenerator {private static int counter = 0; // 静态变量public static synchronized int nextId() {return counter++; // 保护静态状态}
}
5. 错误用法警告
危险组合:混合使用实例锁和类锁
class Resource {// 实例锁public synchronized void instanceMethod() { /* ... */ }// 类锁public static synchronized void staticMethod() { /* ... */ }
}
- 线程A调用
obj.instanceMethod()
- 线程B调用
Resource.staticMethod()
- 不会互斥!因为使用的是不同的锁对象
性能考量
-
类锁的竞争更激烈:
所有实例共享同一把锁,在高并发场景下可能成为瓶颈 -
减小锁粒度建议:
// 不推荐:锁整个方法 public static synchronized void process() { /* 耗时操作 */ }// 推荐:仅锁必要代码段 private static final Object staticLock = new Object();public static void betterProcess() {// 非同步操作...synchronized(staticLock) {// 只同步关键部分} }
程序员面试资料大全|各种技术书籍等资料-1000G
Git高速下载
IDEA开发工具- FREE