我有一个简单的类,它在自己的线程中进行一些计算,并将结果报告给监听器。
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,所以线程总是有成员变量的最新版本,但我不确定。
谢谢!
答案 0 :(得分:5)
是。为了保证Calculator
线程看到一个新值,由另一个线程设置,你必须使变量变为volatile。
但是,volatile
是一种相当低级别的机制,很少用于客户端代码。我建议你考虑在这个场景中使用java.util.concurrent.AtomicReference
,这可以确保这些事情按预期工作。
答案 1 :(得分:3)
如果您使用此方法,则无法保证在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
可能会解决此问题。