Java同步 - 我做得对吗?

时间:2012-06-24 22:28:24

标签: java asynchronous

我不习惯使用synchronized。以下代码段是否正确?

public void setNewSessionListener(NewSessionListener newSessionListener) {
    if (this.newSessionListener != null)
        synchronized (this.newSessionListener) {
            this.newSessionListener = newSessionListener;
        }
    else
        this.newSessionListener = newSessionListener;
}

更具体地说,我需要执行空检查吗?我有一种直觉,认为该代码存在根本性的错误。

4 个答案:

答案 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对象上进行同步,仅为此目的而保留。

此外,最终您在ifelse分支中执行相同的分配,这可能不是您想要的。

答案 2 :(得分:1)

这至少是一个非常糟糕的主意。您正在同步您随后分配的对象。

因为你正在使用synchronized我假设这是异步调用的,它可以被一个线程调用,而另一个线程在这个代码中。如果是这样,你没有锁定一个公共对象,你就锁定了它在那个时间点所持有的值。

可能,我可能会强调,你可以做同步(这个)。这将确保对此特定对象的此方法的所有调用都是同步的。对该类的其他实例的调用是针对该另一个对象锁定的 - 但不是跨越实例。

如果要跨所有实例化对象进行同步,请调用synchronized(YourClass)

答案 3 :(得分:1)

这是另一种可能性(我更喜欢对同步块进行显式锁定):

private ReentrantLock lock = new ReentrantLock();

lock.lock();
try {
  // do your synchronized code here. 
}
finally {
  lock.unlock();
}

虽然只是通过查看你的代码,我不知道为什么甚至有一个if块。为什么你在一个案例中同步,而不是另一个案例?特别是考虑到你在两种情况下做同样的任务?