我有一个数据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调用,或者它是否能够仅通过阻塞来重新分配并且是安全的,如果是这样,如果它是聪明的,则应该这样做。
答案 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
会产生同样的效果。