2011-11-22 38 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條件檢查成功。就我而言,我相信這是同步它的正確選擇。

我的問題是:我應該在這裏聲明listener成員變量爲volatile嗎?我一直在閱讀很多關於volatile的知識,但所有的例子似乎都是針對基本數據類型(boolean,int,...),而不是Object。因此,我不確定對象是否應該/可以被聲明爲不穩定。我相信我必須將其聲明爲volatile,所以線程始終具有最新版本的成員變量,但我不確定。

謝謝!

+0

就像一個建議 - 沒有真正回答你的問題我承認 - 但它會更好地把一個'啓用'類型標誌放在您的監聽器上,然後而不是讓客戶端設置監聽器爲空或非空,它有setEnabled(true)或setEnabled(false)?這會讓你圍繞處理無意的NPE的問題,並且還會阻止你不得不定期實例化新的Listener對象。 –

+0

@Japer D.,這真是一個非常非常醜陋的解決方案。 – mre

+0

@JimKiley我同意你的意見。但是,我簡化了我的問題,因此看起來有點奇怪。爲此道歉。 –

回答

5

是的。爲了保證Calculator線程看到一個新的值,由另一個線程設置,你必須使變量易變。

但是,volatile是一個相當低級的機制,很少用於客戶端代碼。我建議你考慮在這種情況下使用java.util.concurrent.AtomicReference,這可以確保這些事情按預期工作。

+0

好的,謝謝你的確認。我會研究AtomicReference,我不得不承認,我忽略了這個新的併發包太久了。 –

+0

呵呵..我也是,但那是因爲我儘量避免併發性儘可能長:-) – 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(...); 
     } 
    } 
} 

這將運行風險很小,你將後錯過任何活動設置一個非空偵聽器。聲明listenervolatile可能會解決這個問題。

+0

這正是我複製成員變量的原因。如果調用setListener(null),則run()中的局部變量l不會更改。這樣,我避免了同步塊。沒有? –

+0

@TedHopp,不,因爲他將內容複製到本地變量。 – aioobe

+0

@JaperD。 - 我意識到並徹底修改了我的答案。 –