同步

在 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 方法。

以下程式碼塊實際上是等效的(即使位元組碼看起來不同):

  1. this 上的 synchronized 塊:

    public void foo() {
        synchronized(this) {
            doStuff();
        }
    }
    
  2. 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();
    }
}