簡體   English   中英

Java 同步和內存可見性。 10 是否對其他線程可見?

[英]Java synchronization and memory visibility. Is 10 visible to other threads?

我有兩節課。

一個類只是一個帶有 get 和 set 同步方法的整數值的容器。

public class Sync {
   private int dataSync;

   public synchronized int getDataSync() {
       return dataSync;
   }

   public synchronized void setDataSync(int data) {
      dataSync = data;
   }
}

其他班級類似。 它只是一個帶有 get 和 set 方法而沒有同步的整數值的容器。

public class NotSync {

   private int dataNotSync;

   public int getDataNotSync() {
      return dataNotSync;
   }

   public void setDataNotSync(int data) {
      dataNotSync = data;
   }
}

現在我的問題是在 run 方法結束時“是否保證 10 個值對所有其他線程可見”。

public class RunSync {

   public static void main(String[] args) {
      RunSync rs = new RunSync();
      rs.run();
   }

   private NotSync dataNS;

   private Sync    dataS;

   private int     data;

   public RunSync() {
      dataS = new Sync();
      dataNS = new NotSync();
   }

   public synchronized void run() {
      data = 100;
      dataS.setDataSync(45);
      dataNS.setDataNotSync(10);
      //Question A: is 10 value guaranteed to be visible to all other
      //threads when method exits?
      //we are inside a synchronized block aren't we? 
      //so all writes must be flushed to main memory
   }
}

編輯:想象一下還有其他線程。 這只是一個快速編寫的示例。 問題是在同步塊完成時確切保證刷新回主內存的內容。

EDIT2:根據java內存模型“保證一個線程對字段所做的更改僅在某些條件下對其他線程可見。一個條件是寫入線程釋放同步鎖,讀取線程隨后獲取相同的同步鎖。

那么如果另一個線程獲取了 RunSync 鎖,它是否能保證在 RunSync 的 NotSync 實例中看到 10? 即使 NotSync 不受保護?

Edit3:沒有明確答案的相關問題。 我還在找。 使用 volatile 和 synchronized 時,刷新或發布到各個線程的內存范圍是多少?

EDIT4:為了簡化示例,RunSync 類中的此方法如何

public synchronized void run2() {
    dataNS.setDataNotSync(10);
}

當 run2 退出時,什么都不能保證被刷新到主內存? 對此的明確答案將回答我的問題。 如果否,則意味着只有鎖的成員才能保證被刷新,並且如果其他線程在 RunSync 上獲取相同的鎖,則其他線程將可見。 答案是沒有保證。

EDIT5:在這種情況下,斷言是否保證為真?

public class RunSync {

public volatile boolean was10Written = false;

    public synchronized void run2() {
      dataNS.setDataNotSync(10);
      was10Written = true;
   }

   public void insideAnotherThread() {
      if(was10Written) {
         int value = dataNS.getDataNotSync();
         assert value == 10;
      }
   }
}

答案是否定的,不能保證它是可見的,但它可能是可見的。 由於 10 的寫入不是同步的,因此在同步讀取的排序之前不會發生任何情況。

根據 Java 內存模型,“一個線程對字段所做的更改保證僅在某些條件下對其他線程可見。

如果一個線程的寫入發生在同步操作之前,則這是真的。 寫入 10 之后發生,因此沒有可見性保證。

例如,如果您有一個新字段並在寫入dataSync (同步)之前將其寫入為 10,並且您將 dataSync 評估為 45,那么寫入新字段的操作將可見。

還有什么線程? 無論如何,您應該看到關鍵字 volatile 開始。 同樣,同步一種方法不會同步讀/寫的實例變量。 因此,如果一個線程正在寫入,另一個線程可以讀取不同的值,因為只有方法是同步的(一次只有一個線程設置值,一個線程正在讀取,但您可以同時進行讀/寫操作同時

我知道這是一個非常古老的問題,但我認為值得回答。 答案是,RunSync 類原始代碼中 dataNS.dataNotSync 數據成員中的值 10 肯定會被在 rs 對象上同步的任何其他線程可見,並在主線程退出后獲得 rs 的鎖rs.run() 方法。 dataNS.setDataNotSync(10); 調用是在同步塊內(在對象 rs 上)進行的,因此由 JMM,在釋放 rs 鎖之前主線程對任何變量所做的任何寫入都將變得可見(之前發生過)任何線程隨后獲得 rs 上的鎖,然后讀取這些變量。

請注意,任何在主線程進行更改時剛剛休眠的線程,然后嘗試讀取 dataNS 的值而不獲取 rs 上的鎖,都不能保證看到值 10,因為沒有建立之前發生的關系在它們之間讀取 dataNS 和主線程寫入 dataNS。

例如,在下面完成的 main() 方法中,線程 t1 保證打印 10,但這是因為我們在 rs.run() 代碼執行后啟動線程 t1; 如果我們替換了 rs.run() 的順序; 和 t1.start(); 僅當 t1 在 rs.run() 獲得鎖定(並完成)之后獲得鎖定時,才會打印值 10:

public static void main(String[] args) {
    RunSync rs = new RunSync();
    Thread t1 = new Thread() {
        public void run() {
            synchronized(rs) {
                System.out.println(rs.dataNS.getDataNotSync());  
            }  
        }
    };
    rs.run();
    t1.start();
}

如果在上面的代碼中我們寫了“t1.start();” 然后跟着“rs.run()”,為了讓 t1 保證打印 10,我們需要在線程的 run() 中寫一個“while(cond) rs.wait()”語句,它會保證在 rs.run() 從主線程完成執行之前我們不會讀取 dataNS 值,我們需要調用 rs.notify() 作為 rs.run() 方法的最后一條語句。

暫無
暫無

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

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