同步代码块仅用于分配分配还是整个块体?

时间:2012-07-17 14:03:18

标签: java concurrency synchronization java.util.concurrent

我有一个数据var monthArray,它由多个使用者读取,并由一个周期性的预定更新程序线程定期更新。所有都是异步的。

我已经考虑过这两个选项来安全地执行更新。

    ArrayList<String> tempArray = ModelJob.getDistinctMonths(user, true);       
    synchronized (monthArray) {
        monthArray = tempArray;
    }

    synchronized (monthArray) {
        monthArray = ModelJob.getDistinctMonths(user, true);
    }

第一个问题背后的想法是ModelJob.getDistinctMonths(user, true);调用是耗时的,而且我不希望保持同步以阻止更长时间,然后它必须,仅用于快速重新分配旧数组和更新阵列。然而,似乎是一个混淆,我只想做完如果完全nessecary。任何人都可以给我任何洞察jvm如何处理这种同步和天气或不做前者将给我任何增加的性能?基本上我问的是jvm是否会阻止整个静态ModelJob调用,或者它是否能够仅通过阻塞来重新分配并且是安全的,如果是这样,如果它是聪明的,则应该这样做。

5 个答案:

答案 0 :(得分:3)

假设您不需要需要围绕getDistinctMonths()调用的同步(该调用是线程安全的,并且您不需要围绕调用和赋值的原子性),那么你可以只围绕赋值进行同步(是的,阻塞只限于同步块,否则语法将毫无意义)。注意,@ JohnVint提出了一个很好的观点,即在monthArray引用被修改时不应该同步。您必须在不会更改的单独对象实例上进行同步。

最后,您可以移除同步块并使monthArray成员易失,并获得相同的结果。

答案 1 :(得分:1)

如果您只是将volatile修饰符放在monthArray上并删除所有synchronized块,那么您将拥有无锁线程安全性。

此外,JVM可以优化您的清洁(第二)版本的代码,就像它是第一个版本一样。所以,如果保持锁定,最好坚持更清洁的版本。

答案 2 :(得分:1)

从性能的角度来看,使用第一种方法会更好。它将避免不必要的同步。

您必须记住的一件事是,即使是monthArray上的读取也需要同步。仅当使用相同的对象锁同步更新和读取时,同步才有效。我更喜欢使用类Object锁。例如,如果此代码是ModelUpdate类的一部分,则使用以下代码

synchronized(ModelUpdate.class) {
        monthArray = tempArray;
}

答案 3 :(得分:1)

同步块将始终阻止其整个执行。作为参数给出的对象(在您的情况下为monthArray)称为“监视器”,并且将保证具有相同对象(monthArray)的所有其他同步块将作为参数异步执行。

答案 4 :(得分:1)

这里有一个明显的缺陷,就是你不应该同步那些会改变的对象。

我更喜欢第一个例子,而是使用公共锁

final Object lock = new Object();

synchronized(lock){
   monthArr = ...;
}

然而,正如大多数人所说,声明monthArr volatile会产生同样的效果。