如何使用 synchronized?
Synchronized
本质上都是依赖对象来锁,根据不同的对象类型,可以分为三种锁粒度:
this
锁:当前实例锁class
锁:类对象锁Object
锁:对象实例锁
synchronized
关键字的使用方式主要有下面 3 种:
- 修饰实例成员方法:使用
this
锁,线程想要执行被Synchronized
关键字修饰的普通方法,必须先获取当前实例对象的锁资源; - 修饰静态成员方法:使用
class
锁,线程想要执行被Synchronized
关键字修饰的静态方法,必须先获取当前类对象的锁资源; - 修饰代码块:使用
Object
锁,使用给定的对象实现锁功能,线程想要执行被Synchronized
关键字修饰的代码块,必须先获取当前给定对象的锁资源。
1、修饰实例方法 (锁当前对象实例)
给当前对象实例加锁,进入同步代码前要获得 当前对象实例的锁 。
1 | synchronized void method() { |
当一个线程正在访问一个被synchronized
修饰的实例方法时,其他线程则不能访问该对象的其他被synchronized
修饰的对象实例方法,毕竟一个对象只有一把锁,当一个线程获取了该对象的锁之后,其他线程无法获取该对象的锁,所以无法访问该对象的其他被synchronized
修饰的对象实例方法。但是如果有其他方法未被synchronized
修饰,又或者其他被synchronized
修饰的是静态方法,这类方法其他线程还是可以访问的
2、修饰静态方法 (锁当前类)
当synchronized
用于修饰静态方法时,其锁就是当前类的class
对象,当使用class
锁时,当前Java
程序中,一个类只会生成一个class
对象,不会因为new
出多个实例造成多把锁、线程分别获取不同锁资源的情况发生。
这是因为静态成员不属于任何一个实例对象,归整个类所有,不依赖于类的特定实例,被类的所有实例共享。
1 | synchronized static void method() { |
静态 synchronized
方法和非静态 synchronized
方法之间的调用互斥么?
不互斥!如果一个线程 A 调用一个实例对象的非静态 synchronized
方法,而线程 B 需要调用这个实例对象所属类的静态 synchronized
方法,是允许的,不会发生互斥现象,因为访问静态 synchronized
方法占用的锁是当前类的锁,而访问非静态 synchronized
方法占用的锁是当前实例对象锁。
3、修饰代码块 (锁指定对象/类)
对括号里指定的对象/类加锁:
synchronized(object)
表示进入同步代码库前要获得 给定对象的锁,也就是对对象加锁。synchronized(类.class)
表示进入同步代码前要获得 给定 类 的锁,也就是对整个类加锁
这里的 lockObject
是一个对象引用,它可以是任何对象,通常会选择一个与临界资源相关的对象作为锁的对象。当多个线程试图访问同一段被 synchronized
修饰的代码块时,它们必须获取同一个锁对象,否则无法进入临界区
1 | synchronized(lockObject) { |
总结:
synchronized
关键字加到static
静态方法和synchronized(class)
代码块上都是是给 Class 类上锁;synchronized
关键字加到实例方法上是给对象实例上锁;- 尽量不要使用
synchronized(String a)
因为 JVM 中,字符串常量池具有缓存功能