1

このスタックオーバーフローのトピックを読みましたが、その結論は、空の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(...)は1 回だけ呼び出します
  4. さらに、他のスレッド (つまり、上記のスレッドではない) が を呼び出しますuseHandlerInAnotherWay(...)。これはいつでも (mOtherComponent.onMyCustomThreadInitialized()もちろん の後) 発生する可能性があります。

私の質問:

  1. 私が正しければ、はフィールドではないため、mHandler以外のスレッドからアクセスされたときに、最新のデータの可視性を保証する必要があります。これらのいくつかの呼び出しを除いて、同期なしで他のスレッドから使​​用されないため、私もそれを作成したくありません (オーバーヘッドが不要な場所に不必要に存在することは望ましくありません)。つまり、を介してこれらのさらに他のスレッドからアクセスされる場合、「有用なコード」(つまり、実際には相互排除の対象となる必要があるコード) を含む は、呼び出し元のスレッドが正しく認識することも保証します。ただし、では、コードは相互排除を必要としません。myThreadClassfinalvolatilesendMessageWithHandler(..)mHandlervolatilemHandleruseHandlerInAnotherWay()synchronizedmHandlersendMessageWithHandler(..)sendMessageWithHandler(...). これは正しいです?私の問題に対するより良い解決策はありますか?

  2. 私がリンクした他のスタックオーバーフロースレッドには、次の回答があります(受け入れられたものではありませんが、複数回賛成されました):

    以前は、特定のメモリ バリア操作が発生することを仕様が暗示していました。ただし、現在は仕様が変更されており、元の仕様は正しく実装されていません。別のスレッドがロックを解放するのを待つために使用できますが、別のスレッドが既にロックを取得していることを調整するのは難しいでしょう。

    これは、エンプティsynchronizedがメモリバリア機能を提供しなくなったことを意味しますか? についてJavaドキュメントをオンラインで確認するsynchronizedと、すべてのメモリが更新されることが言及されています(つまり、モニターの開始時に「メインメモリ」からスレッドのコピーが更新され、モニターの終了時にスレッドのコピーから「メインメモリ」が更新されます)。しかし、彼らは空のブロックについて何も言及していないsynchronizedので、これは私には不明です.

4

2 に答える 2

2

おそらく同期は必要ありません。

(thread 1)          (thread 2)

write
  |
start thread 2
                 \
                    ...
                     |
                    read  

read確実に見れwriteます。


空のブロックであっても、のセマンティクスsynchronizedは厳密に適用されます。

于 2012-12-05T16:39:28.503 に答える
1

ご覧のとおり、mHandler は作成前に他のスレッドからアクセスされることはなく、存続期間中に変更されることもありません。その結果、同期せずに他のスレッドから安全に読み取ることができます。確実にするために、同期ブロック内で読み取ることができます。

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

「他の各スレッドはsendMessageWithHandler(...)正確に 1 回呼び出す」ため、オーバーヘッドはまったく無視できます。率直に言って、同期機能の使用を最小限に抑えることへの執着 (「揮発性にしたくない」) は、複数のメソッド呼び出しとスレッド作成のバックグラウンドでは不適切に見えます。同期が 1 秒間に何百万回も発生する場合にのみ、気にする価値があります。

于 2012-12-05T17:07:16.967 に答える