簡體   English   中英

java - 我必須將我的共享偵聽器成員變量聲明為volatile嗎?

[英]java - do I have to declare my shared listener member variable as 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(null) 因此,在run()函數中,我創建了一個偵聽器的副本,因此我不能遇到NullPointerException ,如果在!= null條件檢查成功后將偵聽器設置為null,則可能會發生這種情況。 就我而言,我相信這是同步它的正確選擇。

我的問題是:我應該在這里聲明監聽器成員變量為volatile嗎? 我已經閱讀了很多關於volatile的內容,但是所有的例子似乎都是針對基本數據類型(boolean,int,...),而不是Objects。 因此,我不確定對象是否應該/可以被聲明為volatile。 我相信我必須聲明它是volatile,所以線程總是有成員變量的最新版本,但我不確定。

謝謝!

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

但是, volatile是一種非常低級別的機制,很少用於客戶端代碼。 我建議你考慮在這個場景中使用java.util.concurrent.AtomicReference ,這可以確保這些東西按預期工作。

如果使用此方法,則無法保證在setListener(null)返回后偵聽器不會收到事件通知。 執行可以如下進行:

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

// setListener(null) executes here

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

如果需要保證在取消注冊后沒有事件發布到偵聽器,則需要使用synchronized塊。 聲明listener器是volatile也無濟於事。 代碼應該是:

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

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

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

如果您想一直避免synchronized費用,可以這樣做:

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

這會產生一個輕微的風險,即在設置非空偵聽器后您將錯過一個事件。 聲明listener器是volatile可能會解決這個問題。

暫無
暫無

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

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