MartinYeung
MartinYeung

Love life Love IT IT blog: https://ithelp.ithome.com.tw/users/20119569

Java - Volatile keyword的介紹

閱讀時間: 5分鐘

Volatile關鍵字 可以確保在應用程式中的可見性,支持變量直接寫入到主記憶體(main memory)。

來自《Thinking in Java , edtion 4》的解釋

The volatile keyword also ensures visibility across the application. If you declare a field to >be volatile, this means that as soon as a write occurs for that field, all reads will see the >change. This is true even if local caches are involved—volatile fields are immediately written >through to main memory, and reads occur from main memory.

Volatile的主要功能:

第一:提供可見性

當一個變量的前面被加上Volatile後,當它的被修改就會即時被更新到主記憶體(main memory)。

當有其他thread想存取這變量的最新值,都可以在主記憶體(main memory)中看到。

要做到這效果還有其他方法,就是利用Synchronized + Lock來實現。

但不在這裡詳述,會再另一篇文章作講解。

public class WorkerThread extends Thread {
    private boolean isRunning = true;
    @Override
    public void run() {
        while (isRunning) {
            // execute a task
        }
    }
    public void stopWorker() {
        isRunning = false;
    }
}

在上面的例子可以看到它有兩個tasks分別是run() 和stopWorker() 。

變量isRunning 的預設值true,所以可以執行run() 內的動作。

而stopWorker() 則是可以控制能否執行run() ,因為它可以修改isRunning 的值。

但在這個例子中,即使修改了isRunning 的值為false,run()都仍然有機會執行。

重點在於run()能否在主記憶體中獲得最新的isRunning 的值。

為了確保run()能獲得最新的isRunning 的值,我們可以利用Volatile關鍵字來解決。

在變量isRunning前加上volatile:

public class WorkerThread extends Thread {
    private volatile boolean isRunning = true;
    @Overridepublic void run() {
        while (isRunning) {
            // execute a task
        }
    }
    public void stopWorker() {
        isRunning = false;
    }
}

第二:保證初始化對象時的次序(happens-before relationship)

public class MyWorker {

  private int workerNumber;

  public MyWorker (int workerNumber) {
    this.workerNumber = workerNumber;
  }

  public int getWorkerNumber () {
    return workerNumber;
  }

}

在上面的例子,我們可以實例化MyWorker class (同時假設這是其中一個Thread,名字叫Thread 1)

MyWorker workerInstance = new MyWorker (1);

當執行之上的code時,JVM 會依以下次序操作:

1: 分配記憶體的空間

2: 將分配到的記憶體的空間(記憶體的空間的地址)給予實例workerInstance

(workerInstance 不再是null了)

3: 將值寫入workerInstance

不過在第2個與第3個步驟之間會有機會遇到其他Thread正在執行以下程式:

if(workerInstance!= null){
  System.out.println(workerInstance.getValue());
}

所以最後由Thread 1實例化MyWorker class的結果不是1。

為了解決上述的問題,我們可以在變量workerNumber前加上volatile關鍵字。

可以保證JVM只能在完成所有寫入程序後(完成第3個步驟後)才能讀取workerInstance的值。

public class MyWorker {

  private volatile int workerNumber;

  public MyWorker (int workerNumber) {
    this.workerNumber = workerNumber;
  }

  public int getWorkerNumber () {
    return workerNumber;
  }

}

參考文章/網站/書本:


CC BY-NC-ND 2.0 版权声明

喜欢我的文章吗?
别忘了给点支持与赞赏,让我知道创作的路上有你陪伴。

加载中…

发布评论