簡體   English   中英

重新訪問空的同步塊:數據可見性

[英]Empty synchronized block revisited: data visibility

我讀了這個 stackoverflow主題,結論似乎是一個空的synchronized塊總是可以避免使用更好的解決方案。 這個話題對我來說也有一些不清楚的部分,我將整合到我的下面的帖子中。

假設我們有一個這樣的類:

public class MyThreadClass extends Thread {
  private final OtherComponent mOtherComponent;
  private final Object mLock = new Object();
  private MyHandler mHandler;

  public MyCustomThread(OtherComponent otherComponent) {
      mOtherComponent = otherComponent;      

  public void run() {

      mHandler = new Handler() {...}

      // Other init operations

      mOtherComponent.onMyCustomThreadInitialized();

      // ... other operations (Looper.loop(), actually)
  }

  public void sendMessageWithHandler(int what, int arg) {
       synchronized (mLock) {}
       Message msg = mHandler.obtainMessage(what);
       msg.arg1 = arg;
       mHandler.sendMessage(msg);
  }

  public void useHandlerInAnotherWay(...) {
       synchronized (mLock) {
            // useful code that needs actual mutual exclusion
       }
       mHandler.sendMessage(...);
  }
}

我的應用程序的相關部分以下列方式工作:

  1. MyThreadClass線程已創建並啟動。
  2. 作為mOtherComponent.onMyCustomThreadInitialized()的間接結果,我的應用程序的另一部分將開始產生其他線程 (注意它們不是從這個調用同步啟動的,這就是為什么我說這是間接后果。 唯一的一點是, mHandler已經被這些其他線程啟動時初始化
  3. 其他每個線程都會調用sendMessageWithHandler(...)一次
  4. 然而,其他線程(即不是上面提到的線程)再次調用useHandlerInAnotherWay(...) ,這可能在任何時候發生mOtherComponent.onMyCustomThreadInitialized()當然在mOtherComponent.onMyCustomThreadInitialized()之后)。

我的問題:

  1. 如果我是正確的,最多時最新數據可見性必須得到保證mHandler從其他線程訪問比myThreadClass ,因為它不是一個final場。 我也不想讓它變得volatile ,因為除了這幾個sendMessageWithHandler(..)調用之外, mHandler沒有在沒有同步的情況下從其他線程中使用(我不希望在不必要的地方不必要地存在volatile開銷)。 換句話說,當mHandler已經從那些還沒有其他線程通過訪問useHandlerInAnotherWay()時, synchronized的“有用的代碼”(即實際需要的是相互排斥的對象,即代碼)存在也保證了呼叫者線程看到mHandler正確。 但是,在sendMessageWithHandler(..) ,代碼不需要互斥,所以我決定將一個空的synchronized塊放到sendMessageWithHandler(...)的開頭。 這個對嗎? 我的問題有更好的解決方案嗎?

  2. 我鏈接到的另一個stackoverflow線程有以下答案(它不是被接受的,但被多次upvoted):

    過去,規范暗示某些內存屏障操作發生。 但是,規范現在已經改變,原始規范從未正確實現。 它可能用於等待另一個線程釋放鎖,但協調另一個線程已經獲得鎖定將是棘手的。

    這是否意味着空synchronized不再提供內存屏障功能? 如果我在線查看關於synchronized的Java文檔,他們會提到由於它而更新了所有內存(即,在監視器輸入時從“主存儲器”更新線程副本,並且在監視器出口處從“線程副本”更新“主存儲器”)。 但是他們沒有提到關於空synchronized塊的任何事情,所以我不清楚這一點。

您可能不需要任何同步。

(thread 1)          (thread 2)

write
  |
start thread 2
                 \
                    ...
                     |
                    read  

read保證看到write


即使它是一個空塊,也嚴格執行synchronized的語義。

正如我所看到的,mHandler在創建之前不會被其他線程訪問,並且在生命周期內不會被更改。 因此,可以安全地從其他線程讀取它而無需任何同步。 要絕對確定,您可以在synchronized塊中讀取它:

public void sendMessageWithHandler(int what, int arg) {
   MyHandler mHandler;
   synchronized (mLock) {
      mHandler=this.mHandler;
   }
   // the rest of code unchanged
}

由於“其他每個線程都會調用sendMessageWithHandler(...)一次”,因此開銷幾乎可以忽略不計。 坦率地說,你對最小化同步設施的使用(“我不想讓它變得易失”)的痴迷在多個方法調用和線程創建的背景下看起來不合適。 只有當同步每秒發生數百萬次時才值得打擾。

暫無
暫無

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

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