Java 中的 volatile 關鍵字

Mohd Mohtashim Nawaz 2023年10月12日
  1. Java 中的 volatile 關鍵字
  2. 在 Java 中我們什麼時候使用 volatile 關鍵字
  3. Java 中 volatile 關鍵字的效能優勢
  4. 在 Java 中使用 volatile 關鍵字
  5. まとめ
Java 中的 volatile 關鍵字

Java 是一種非常流行的程式語言,通過了解 Java,我們可以很容易地理解它為什麼會在程式設計社群中獲得這樣的地位。

Java 為我們提供了大量有用的資源,我們可以在程式中使用這些資源來編寫所需的邏輯。

Java 對於多用途程式設計問題很有用,其中一個這樣的程式設計問題是使用執行緒或不同邏輯程式的並行執行。

本文將探索和理解 Java 中的 volatile 關鍵字、它的屬性以及它在實際程式設計中的用法。我們將瞭解何時使用 volatile 關鍵字、其重要性以及何時以及不應使用。

本文還將提供一個基本的示例程式碼來幫助你理解 volatile 關鍵字。

Java 中的 volatile 關鍵字

在使用 Java 時,你可能遇到過並行執行緒和 synchronized 關鍵字。volatile 關鍵字與 synchronized 關鍵字相似,但又完全不同。

volatile 關鍵字在我們的程式碼中很容易理解和實現,並且比 synchronized 鎖定機制具有更少的執行時開銷。然而,volatile 關鍵字不如 synchronized 鎖定強大。

volatile 關鍵字確保對共享 volatile 變數所做的更改立即反映在所有執行緒中。synchronized 關鍵字用於實現 Java 中的鎖機制,鎖提供可見性和原子性。

但是,volatile 關鍵字只能實現可見性,不能實現原子性。因此,我們可以理解,volatile 關鍵字只能在極少數情況下使用。

例如,當變數的值獨立於所有其他變數和案例,甚至獨立於自身時,我們可以使用它。

在 Java 中我們什麼時候使用 volatile 關鍵字

正如我們已經看到的,在 volatile 關鍵字的幫助下,共享變數的更改值會立即反映在所有其他執行緒中。因此,我們在處理執行緒中的共享變數時使用它。

synchronized 關鍵字已經可用時,你可能會問為什麼我們使用 volatile 關鍵字?但是,volatile 關鍵字更易於理解且效能更好。

因此,在某些情況下,我們可能想要使用它。例如,當我們有大量的讀取操作和少量的寫入操作時,我們希望使用 volatile 關鍵字以獲得更好的效能。

此外,volatile 關鍵字提供了更好的可伸縮性,因為它不會導致執行緒阻塞。

為了更好地理解我們可以安全有效地使用 volatile 關鍵字的情況,我們應該注意以下情況。

  • 程式的當前和其他變數和狀態之間不應存在相互依賴關係。
  • 變數不應依賴於自身。

因此,如果我們的變數完全獨立於自身,我們可以使用程式碼的 volatile 關鍵字。

但是,我們經常遇到不滿足第一個或第二個條件或兩個條件的情況。因此,volatile 關鍵字的使用受到很大限制。

請注意許多程式設計師犯的一個非常微妙的錯誤。很多時候,程式設計師傾向於將增量運算子視為單個操作。

但是,增量運算子(或減量運算子)由多個操作組成,並且發生在多個階段。因此,在這種情況下,我們不應該使用 volatile 關鍵字。

Java 中 volatile 關鍵字的效能優勢

雖然 volatile 關鍵字簡單易用,但它的效能優於鎖定機制。在某些情況下,可以看出 volatile 的效能比 synchronized 好得多。

然而,事情並沒有那麼簡單。當我們有 Java 虛擬機器(JVM)時,很難正確判斷這兩個關鍵字的效能。

是因為 JVM 進行了優化,有時候加鎖是不需要的,JVM 在程式碼優化的時候把它去掉了。但是,通常觀察到易失性讀取在效能方面非常好,並且可以與普通讀取相媲美。

然而,易失性寫入比普通寫入要昂貴得多。另一方面,易失性寫入總是比獲取鎖便宜。

因此,我們應該考慮 volatile 關鍵字和 synchronized 鎖定機制之間的效能比較。

我們已經討論了 volatile 的另一個效能優勢:它不會導致執行緒阻塞並且在可擴充套件性方面要好得多。這是使用 volatile 關鍵字的另一個原因。

在 Java 中使用 volatile 關鍵字

我們將在下面的程式碼中瞭解如何在我們的 Java 程式碼中使用 volatile 關鍵字。

sharedObj.java

public class sharedObj {
  volatile boolean flag;
}

ThClass.java

public class ThClass extends Thread {
  public sharedObj obj;
  public ThClass(sharedObj obj) {
    this.obj = obj;
  }

  @Override
  public void run() {
    super.run();
    while (true) {
      if (this.obj.flag)
        this.obj.flag = false;
      else
        this.obj.flag = true;
      System.out.println(obj.flag);
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

主類.java

import java.util.*;
public class MainClass {
  public static void main(String[] args) {
    sharedObj obj = new sharedObj();
    ThClass th1, th2;
    th1 = new ThClass(obj);
    th2 = new ThClass(obj);
    th1.run();
    th2.run();
  }
}

在上面給出的 Java 程式中,我們建立了三個不同的類。這些類解釋如下。

  • sharedObj.java:我們已經宣告瞭一個在這個類中的執行緒之間共享的可變共享變數。
  • ThClass.java:這個類用於建立執行緒。它擴充套件了 Thread 類並覆蓋了 run() 方法。每個執行緒休眠 1 秒。
  • MainClass.javaMain 類是實現 main() 方法的最重要的類,從執行開始。我們建立了兩個執行緒,並通過建構函式傳遞了一個共享物件作為引數。

該 Java 程式的輸出如下所示。

true
false
true
false
true
false

請注意,此程式碼執行無限時間,要停止它,你必須手動停止它的執行。 (例如,在 Linux 上按 CTRL+C)。

まとめ

在本文中,我們瞭解了 volatile 關鍵字、它的優缺點以及何時可以使用它。雖然 volatile 關鍵字不如同步鎖定機制強大,但它仍然在實踐中使用。

我們仍然使用 volatile 關鍵字,因為它在程式碼中更容易理解和實現,比鎖具有更好的效能,並且更容易擴充套件,因為它不鎖定執行緒。

但是,在程式碼中使用 volatile 關鍵字時,應始終小心檢查條件,以免發生意外錯誤。

本文提到了檢查 volatile 關鍵字是否適用的簡單步驟;檢查使用這些步驟以瞭解 volatile 是否適用於你的程式碼。

這將有助於在同步鎖定機制和 volatile 關鍵字之間進行選擇。