同步
在 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();
}
}