2011-11-22 4 views
5

私は、独自のスレッドでいくつかの計算を行い、その結果をリスナーに報告する単純なクラスを持っています。いずれかの時点でjava - 共有リスナーメンバー変数をvolatileとして宣言する必要がありますか?

class Calculator extends Thread { 
    protected Listener listener; 

    public void setListener(Listener l) { 
     listener = l; 
    } 

    public void run() { 
     while (running) { 
      ... do something ... 

      Listener l = listener; 

      if (l != null) { 
       l.onEvent(...); 
      } 
     } 
    } 
} 

彼は一定の期間のためのイベントを望んでいない場合、ユーザーはsetListener(ヌル)を呼び出すことができます。したがって、のrun()関数では、私はNullPointerExceptionに入ることができないので、リスナーのコピーを作成します。!= nullの後にリスナーがnullに設定された場合に起こる可能性がある条件チェックが成功しました。私の場合は、これを同期するための正しい選択だと私は信じています。

私の質問は、ここでリスナーメンバー変数をvolatileとして宣言する必要がありますか?私は揮発性について多くのことを読んできましたが、すべての例はオブジェクトではなく、基本データ型(boolean、int、...)を対象としています。だから、私はオブジェクトが揮発性であると宣言されるべきかどうかはわかりません。私は揮発性と宣言しなければならないと信じているので、スレッドは常にメンバ変数の最新バージョンを持っていますが、わかりません。

ありがとうございます!

+0

あなたの質問に本当に答えるわけではありませんが、リスナーに '有効'タイプのフラグを設定して、クライアントにリスナーのnullまたはnot-nullを設定させるのではなく、setEnabled(true)またはsetEnabled(false)していますか?これにより、不注意なNPEを処理するという問題が回避され、定期的に新しいListenerオブジェクトをインスタンス化する必要がなくなります。 –

+0

@Japer D.、それは本当に、本当に、あなたがそこに持っている醜い解決策です。 – mre

+0

@JimKiley私はあなたに同意します。しかし、私は私の質問を単純化していたので、少し奇妙に見えます。それについての謝罪。 –

答えて

5

はい。 Calculatorスレッドが別のスレッドによって設定された新しい値を保証するためには、変数をvolatileにする必要があります。

しかし、volatileは非常に低レベルのメカニズムであり、クライアントコードで使用されることはめったにありません。このシナリオでjava.util.concurrent.AtomicReferenceを使用することを検討することをお勧めします。これにより、これらのことが期待どおりに機能することが確認されます。

+0

申し訳ありません、確認していただきありがとうございます。私はAtomicReferenceを調べますが、私は新しい並行パッケージをあまりにも長く無視したことを認めなければなりません。 –

+0

Hehe ..私もそうですが、可能な限り並行性を完全に避けようとしているからです:-) – aioobe

+0

良い考え:) –

3

この方法を使用すると、setListener(null)が返された後にリスナーがイベント通知を受信しないことが保証されなくなります。次のように実行は続行できます

Listener l = listener; // listener != null at this point 

// setListener(null) executes here 

if (l != null) { 
    l.onEvent(...); 
} 

をあなたはそれが未登録になった後はイベントがリスナーに掲載されないことを保証する必要がある場合は、あなたがsynchronizedブロックを使用する必要があります。 listenervolatileとすることは役に立ちません。コードは、代わりに次のようになります。

public synchronized void setListener(Listener l) { 
    listener = l; 
} 

public void run() { 
    while (running) { 
     ... do something ... 

     synchronized (this) { 
      if (listener != null) { 
       listener.onEvent(...); 
      } 
     } 
    } 
} 

あなたが​​すべての時間の費用を避けたい場合は、あなたがこれを行うことができます:

if (listener != null) { 
    synchronized (this) { 
     if (listener != null) { 
      listener.onEvent(...); 
     } 
    } 
} 

をこれはあなたが後にイベントを欠場する若干のリスクを実行しますnull以外のリスナーを設定します。 listenervolatileとすると、それはおそらく修正されます。

+0

これは、メンバー変数をコピーする理由です。 setListener(null)が呼び出された場合、run()のローカル変数lは変更されません。このようにして、私は同期ブロックを避けます。いいえ? –

+0

@TedHopp、no、彼はコンテンツをローカル変数にコピーするので、いいえ。 – aioobe

+0

@ JaperD。 - 私はそれを認識し、私の答えを完全に改訂しました。 –

関連する問題