簡體   English   中英

如果2個不同的寫入和讀取線程永遠不會同時存在,我是否需要使用volatile

[英]Do I need to use volatile, if 2 different write and read thread will never alive at the same time

通過參考http://www.javamex.com/tutorials/synchronization_volatile.shtml由於附加規則3 ,我不確定在下列情況下是否需要使用volatile關鍵字

  1. 原始靜態變量將由線程A寫入。
  2. 線程B將讀取相同的原始靜態變量。
  3. 線程B只會在線程A“死”后運行。 (“死”表示,線程A的無效運行的最后一個語句已完成)

線程A寫入的新值是否會在“死”之后始終提交給主內存? 如果是,是否意味着如果滿足上述3個條件,我不需要volatile關鍵字?

我懷疑在這種情況下需要volatile 根據需要, ArrayList可能會損壞。 由於一個線程可以執行insert和update size成員變量。 之后,另一個線程(不兼容)可能會讀取ArrayListsize 如果查看ArrayList源代碼,則不ArrayList size聲明為volatile。

ArrayList JavaDoc中,只提到ArrayList不能安全地用於多個線程同時訪問ArrayList實例 ,但不能讓多個線程在不同的時間訪問ArrayList實例

讓我使用以下代碼來解決此問題

public static void main(String[] args) throws InterruptedException {
    // Create and start the thread
    final ArrayList<String> list = new ArrayList<String>();
    Thread writeThread = new Thread(new Runnable() {
        public void run() {
            list.add("hello");
        }
    });
    writeThread.join();
    Thread readThread = new Thread(new Runnable() {
        public void run() {
            // Does it guarantee that list.size will always return 1, as this list
            // is manipulated by different thread?
            // Take note that, within implementation of ArrayList, member
            // variable size is not marked as volatile.
            assert(1 == list.size());
        }
    });
    readThread.join();
}

是的,您仍然需要使用volatile(或其他形式的同步)。

原因是兩個線程可以在不同的處理器上運行,即使一個線程在另一個線程開始之前已經完成了很長時間,也不能保證第二個線程在進行讀取時會獲得最新的值。 如果該字段未標記為volatile並且未使用其他同步,則第二個線程可以獲取在其運行的處理器上本地緩存的值。 理論上緩存的值可能在很長一段時間內都是過時的,包括在第一個線程完成之后。

如果使用volatile,則將始終將值寫入主內存並從主內存中讀取,從而繞過處理器的緩存值。

不,你可能不需要它。 盡管Mark Byers的回答開始相當准確,但它是有限的。 synchronized和volatile不是在線程之間正確傳遞數據的唯一方法。 還有其他一些較少談論“同步點”。 具體而言,線程開始和線程結束同步點。 但是,啟動線程B的線程必須已經識別出線程A已完成(例如,通過連接線程或檢查線程的狀態)。 如果是這種情況,則變量不需要是volatile。

可能是的,除非您手動創建內存屏障。 如果A設置變量,並且B決定從某個注冊表中取出oit,那么就會出現問題。 因此,您需要一個內存屏障,隱式(鎖定,易失性)或顯式。

http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.4.4

線程T1中的最終操作與另一個檢測到T1已終止的線程T2中的任何操作同步。 T2可以通過調用T1.isAlive()或T1.join()來完成此操作。

因此,可以在不使用volatile的情況下實現目標。

在許多情況下,當存在明顯的時間依賴性時,同事正在由人員進行同步,並且應用程序不需要額外的同步。 不幸的是,這不是規則,程序員必須仔細分析每個案例。

一個例子是Swing工作者線程。 人們會在工作線程中進行一些計算,將結果保存到變量,然后引發事件。 然后,事件線程將從變量中讀取計算結果。 應用程序代碼不需要顯式同步,因為“引發事件”已經進行了同步,因此工作線程的寫入在事件線程中是可見的。

一方面,這是一種幸福。 另一方面,許多人不理解這一點,他們省略了同步只是因為他們從未想過這個問題。 他們的節目恰好是正確的......這一次。

如果線程A在線程B開始讀取之前肯定死亡,則可以避免使用volatile

例如。

public class MyClass {

   volatile int x = 0;

   public static void main(String[] args) {

      final int i = x;
      new Thread() {
         int j = i;
         public void run() {
            j = 10;
            final int k = j;
            new Thread() {
               public void run() {
                  MyClass.x = k;
               }               
            }.start();
         }
      }.start();
   }
}

但問題是,無論Thread啟動哪個線程B,現在都需要線程A寫入的值已經改變,並且不使用自己的緩存版本。 最簡單的方法是讓線程A生成線程B.但是如果線程A在生成線程B時沒有別的事情可做,那么這似乎有點無意義(為什么不使用相同的線程)。

另一個替代方案是,如果沒有其他線程依賴於此變量,則可能線程A可以使用volatile變量初始化局部變量,執行它需要執行的操作,然后最終將其局部變量的內容寫回volatile變量。 然后,當線程B啟動時,它從volatile變量初始化其局部變量,然后僅從其局部變量讀取。 這應該大大減少保持volatile變量同步所花費的時間。 如果這個解決方案似乎不可接受(因為其他線程寫入volatile變量或其他),那么你肯定需要聲明變量volatile。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM