我不习惯使用synchronized。以下代码段是否正确?
public void setNewSessionListener(NewSessionListener newSessionListener) {
if (this.newSessionListener != null)
synchronized (this.newSessionListener) {
this.newSessionListener = newSessionListener;
}
else
this.newSessionListener = newSessionListener;
}
更具体地说,我需要执行空检查吗?我有一种直觉,认为该代码存在根本性的错误。
答案 0 :(得分:3)
有两个错误。第一个是如果您访问需要同步的字段,则始终必须使用相同的锁来访问它。此外,您还必须检查该字段是否为空并写入同一个同步块中的字段,否则当您向该字段写入内容时,它可能已经不为空。
第二个是最好在不改变的东西上进行同步,换句话说,在静态最终字段或实例本身上进行同步。例如,您可以专门为此目的创建一个锁定对象:
private static final Object LOCK = new Object();
然后你会写:
synchronized (LOCK) {
if (this.newSessionListener == null) this.newSessionListener = newSessionListener;
}
答案 1 :(得分:2)
你的感觉是对的。您应该在synchronized
块内进行空检查。否则该块不会阻止双重初始化。此外,您不应在要更改的this.newSessionListener
上进行同步 - 选择一个对象(引用),该对象将在整个块的范围内保留。这是保证只有一个线程可以在任何时间点进入该代码块的唯一方法。实现此目标的典型方法是synchronize
this
。或者,您可以在private final
对象上进行同步,仅为此目的而保留。
此外,最终您在if
和else
分支中执行相同的分配,这可能不是您想要的。
答案 2 :(得分:1)
这至少是一个非常糟糕的主意。您正在同步您随后分配的对象。
因为你正在使用synchronized我假设这是异步调用的,它可以被一个线程调用,而另一个线程在这个代码中。如果是这样,你没有锁定一个公共对象,你就锁定了它在那个时间点所持有的值。
可能,我可能会强调,你可以做同步(这个)。这将确保对此特定对象的此方法的所有调用都是同步的。对该类的其他实例的调用是针对该另一个对象锁定的 - 但不是跨越实例。
如果要跨所有实例化对象进行同步,请调用synchronized(YourClass)
答案 3 :(得分:1)
这是另一种可能性(我更喜欢对同步块进行显式锁定):
private ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// do your synchronized code here.
}
finally {
lock.unlock();
}
虽然只是通过查看你的代码,我不知道为什么甚至有一个if块。为什么你在一个案例中同步,而不是另一个案例?特别是考虑到你在两种情况下做同样的任务?