同步
在 Java 中,有一个内置的语言级锁定机制:synchronized
块,它可以使用任何 Java 对象作为内部锁(即每个 Java 对象可能有一个与之关联的监视器)。
内在锁为语句组提供原子性。要了解这对我们意味着什么,让我们看看 synchronized
有用的示例:
private static int t = 0;
private static Object mutex = new Object();
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(400); // The high thread count is for demonstration purposes.
for (int i = 0; i < 100; i++) {
executorService.execute(() -> {
synchronized (mutex) {
t++;
System.out.println(MessageFormat.format("t: {0}", t));
}
});
}
executorService.shutdown();
}
在这种情况下,如果不是 synchronized
块,则会涉及多个并发问题。第一个是 post 增量运算符(它本身不是原子),第二个是在任意数量的其他线程有机会修改它之后我们将观察 t 的值。但是,由于我们获得了内部锁定,因此此处不存在竞争条件,输出将按正常顺序包含 1 到 100 之间的数字。
Java 中的内部锁是互斥锁 (即相互执行锁)。相互执行意味着如果一个线程已获得锁定,则第二个线程将被强制等待第一个线程释放它,然后才能为自己获取锁定。注意:可能使线程进入等待(休眠)状态的操作称为阻塞操作。因此,获取锁定是阻止操作。
Java 中的内部锁是可重入的。这意味着如果一个线程试图获取它已经拥有的锁,它将不会阻塞并且它将成功获取它。例如,以下代码在调用时不会阻塞:
public void bar(){
synchronized(this){
...
}
}
public void foo(){
synchronized(this){
bar();
}
}
除了 synchronized
区块外,还有 synchronized
方法。
以下代码块实际上是等效的(即使字节码看起来不同):
-
this
上的synchronized
块:public void foo() { synchronized(this) { doStuff(); } }
-
synchronized
方法:public synchronized void foo() { doStuff(); }
同样对于 static
方法,这个:
class MyClass {
...
public static void bar() {
synchronized(MyClass.class) {
doSomeOtherStuff();
}
}
}
具有与此相同的效果:
class MyClass {
...
public static synchronized void bar() {
doSomeOtherStuff();
}
}