繁体   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