Java 中的 synchronized 块用来标记方法或者代码块是同步的。synchronized 可以用来避免竞态条件(Race Conditions)。
通常有四种不同的同步块:
- 实例方法 (Instance methods)
- 静态方法 (Static methods)
- 实例方法中的代码块 (Code blocks inside instance methods)
- 静态方法中的代码块 (Code blocks inside static methods)
同步实例方法 (Synchronized Instance methods)
在实例方法上使用 synchronized:
public synchronized void add(int value){
count += value;
}
synchronized 关键字修饰 add 方法,表示该方法同步。
同步的实例方法,是同步在拥有该方法的对象实例(Instance)上。因此,对于每一个实例,同一时间只有一个线程可以执行该实例的 synchronized 方法。如果有多个实例存在,那么每一个实例都有自己的锁,互不影响。
也就是:一个对象的一个 synchronized 方法只能由一个线程访问。
同步静态方法 (Synchronized Static methods)
同步静态方法是同步在类对象(Class Object)上。因为在 Java VM 中每一个类只有一个 Class 对象存在,所以同一时间只有一个线程可以执行同一个类的静态同步方法。
public static MyStaticCounter {
private static int count = 0;
public static synchronized void add(int value){
count += value;
}
}
对于包含静态同步方法的类,即使有多个实例,所有实例也共享同一个类锁。
如果一个类包含多个静态同步方法,那么同一时间只有一个线程可以执行其中任意一个方法:
public static MyStaticCounter{
private static int count = 0;
public static synchronized void add(int value){
count += value;
}
public static synchronized void subtract(int value){
count -= value;
}
}
如果线程 A 在执行 add() 方法,那么线程 B 既不能执行 add() 也不能执行 subtract(),因为它们都需要获取同一个类对象的锁。
实例方法中的同步代码块 (Synchronized Blocks in Instance Methods)
有时你不需要同步整个方法,只需要同步方法中的一部分。这时可以使用同步代码块。
public void add(int value){
synchronized(this){
this.count += value;
}
}
synchronized(this) 锁住的是当前对象实例。这与同步整个实例方法的效果是一样的,但粒度更细,可以提高性能(只锁住需要同步的代码段)。
当然,也可以同步在其他对象上:
public class MyClass {
private final Object lock = new Object();
public void add(int value){
synchronized(lock){
this.count += value;
}
}
}
静态方法中的同步代码块 (Synchronized Blocks in Static Methods)
同理,在静态方法中也可以使用同步代码块:
public class MyClass {
public static synchronized void log1(String msg1, String msg2){
log.writeln(msg1);
log.writeln(msg2);
}
public static void log2(String msg1, String msg2){
synchronized(MyClass.class){
log.writeln(msg1);
log.writeln(msg2);
}
}
}
这里 synchronized(MyClass.class) 锁住的是 MyClass 的类对象。这与 static synchronized 方法使用的是同一个锁。
对象锁 vs 类锁 (Object level lock vs Class level lock)
对象级别锁 (Object Level Lock)
对象锁是针对某个具体实例的锁。
- 同步实例方法:
public class DemoClass { public synchronized void demoMethod(){} } - 同步代码块(锁 this):
public class DemoClass { public void demoMethod(){ synchronized (this) { // ... } } } - 同步代码块(锁特定对象):
public class DemoClass { private final Object lock = new Object(); public void demoMethod(){ synchronized (lock) { // ... } } }
类级别锁 (Class Level Lock)
类锁是针对 Class 对象的锁,在该类的所有实例之间共享。
- 同步静态方法:
public class DemoClass { public synchronized static void demoMethod(){} } - 同步代码块(锁 .class):
public class DemoClass { public void demoMethod() { synchronized (DemoClass.class) { // ... } } } - 同步代码块(锁静态对象):
public class DemoClass { private final static Object lock = new Object(); public void demoMethod() { synchronized (lock) { // ... } } }
总结
- synchronized 实例方法:锁当前实例对象。
- synchronized 静态方法:锁当前类的 Class 对象。
- synchronized 代码块:可以指定锁对象(实例或 Class 对象)。