Synchronized锁的使用方式
文章目录
- 一、修饰实例方法(对象锁)
- 示例:
- 特点:
- 二、修饰静态方法(类锁)
- 示例:
- 特点:
- 三、修饰代码块(对象锁/类锁)
- 1. 锁对象为实例对象(`this`)
- 2. 锁对象为类对象(`XXX.class`)
- 3. 锁对象为自定义对象(推荐)
- 特点:
- 四、使用注意事项
- 总结
在Java中,
synchronized
是一种内置锁机制,用于实现多线程间的同步,保证临界区代码的原子性和可见性。其使用方式主要有以下四种,适用于不同的同步场景:
一、修饰实例方法(对象锁)
当
synchronized
修饰实例方法时,锁的对象是当前类的实例对象(this
)。多个线程访问同一个实例的同步方法时,会竞争该实例的锁,同一时间只有一个线程能执行。
示例:
public class SyncDemo {// 同步实例方法,锁为当前对象(this)public synchronized void instanceMethod() {// 临界区代码(如操作共享资源)System.out.println(Thread.currentThread().getName() + "执行同步方法");try {Thread.sleep(1000); // 模拟耗时操作} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) {SyncDemo demo = new SyncDemo();// 两个线程访问同一个实例的同步方法,会串行执行new Thread(demo::instanceMethod, "线程1").start();new Thread(demo::instanceMethod, "线程2").start();}
}
特点:
- 锁的是对象实例,不同实例间的同步方法互不干扰(如
new SyncDemo()
和new SyncDemo()
的同步方法可并行执行)。- 同一实例的多个同步方法共享同一把锁(一个线程执行
methodA()
时,其他线程无法执行该实例的methodB()
)。
二、修饰静态方法(类锁)
当
synchronized
修饰静态方法时,锁的对象是当前类的Class对象(每个类只有一个Class对象)。多个线程访问该类的任何静态同步方法时,都会竞争这把类锁。
示例:
public class SyncDemo {// 同步静态方法,锁为当前类的Class对象(SyncDemo.class)public static synchronized void staticMethod() {System.out.println(Thread.currentThread().getName() + "执行静态同步方法");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) {// 两个线程访问静态同步方法,无论是否同一实例,都会串行执行new Thread(SyncDemo::staticMethod, "线程1").start();new Thread(SyncDemo::staticMethod, "线程2").start();}
}
特点:
- 锁的是类对象,所有实例共享这把锁(即使
new SyncDemo()
多个实例,静态同步方法仍串行执行)。- 静态同步方法与实例同步方法使用不同的锁(互不干扰,可并行执行)。
三、修饰代码块(对象锁/类锁)
synchronized
代码块可以指定自定义锁对象,只对该代码块加锁,粒度更细,灵活性更高。根据锁对象的类型,可分为:
1. 锁对象为实例对象(this
)
与同步实例方法的锁一致,控制同一实例对代码块的并发访问。
public class SyncDemo {public void method() {// 同步代码块,锁为当前对象(this)synchronized (this) {System.out.println(Thread.currentThread().getName() + "执行同步代码块");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
2. 锁对象为类对象(XXX.class
)
与同步静态方法的锁一致,控制所有实例对代码块的并发访问。
public class SyncDemo {public void method() {// 同步代码块,锁为类对象(SyncDemo.class)synchronized (SyncDemo.class) {System.out.println(Thread.currentThread().getName() + "执行同步代码块");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
3. 锁对象为自定义对象(推荐)
使用专门的锁对象(如
Object
实例),避免与其他同步方法/代码块共享锁,减少锁竞争。
public class SyncDemo {// 自定义锁对象(推荐使用private final修饰,避免被修改)private final Object lock = new Object();public void method() {// 同步代码块,锁为自定义对象locksynchronized (lock) {// 临界区代码}}
}
特点:
- 粒度更细:仅对需要同步的代码块加锁,其他代码可并行执行,性能更高。
- 灵活可控:可通过不同的锁对象实现多组独立同步(如
lock1
控制A代码块,lock2
控制B代码块,互不干扰)。
四、使用注意事项
锁的可重入性:
synchronized
是可重入锁,同一线程可多次获取同一把锁(如同步方法中调用同一对象的其他同步方法,不会死锁)。锁的释放:
线程执行完同步代码(正常退出或抛出异常)后,会自动释放锁,无需手动操作。避免锁竞争过度:
- 同步范围不宜过大(如只锁必要的代码块,而非整个方法),减少线程阻塞时间。
- 避免使用
String
常量或Integer
等包装类作为锁对象(可能因常量池导致锁共享)。
- 与
wait()
/notify()
配合:
在同步代码块中,可通过锁对象.wait()
让线程等待,锁对象.notify()
唤醒线程,实现线程协作(如生产者-消费者模型)。
总结
synchronized
的三种使用方式各有适用场景:
- 实例方法:控制同一实例的方法级同步,简单直接。
- 静态方法:控制类级别的同步,适用于静态资源的并发访问。
- 代码块:粒度更细,可自定义锁对象,灵活性最高,推荐优先使用。
合理选择使用方式,可在保证线程安全的同时,减少性能损耗。