2

我有一个数据 var,monthArray由多个消费者读取,并由单个定期更新程序线程定期更新。都是异步的。

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

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

或者

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

第一个背后的想法是ModelJob.getDistinctMonths(user, true);调用很耗时,我不想让同步阻塞更长的时间,只是为了用更新的数组快速重新分配旧数组。然而似乎是一个混淆,我只想在完全必要的情况下这样做。谁能给我任何关于jvm如何处理这种同步和天气的见解,或者不做前者会让我的性能有所提高吗?基本上我问jvm是否会阻塞整个静态ModelJob调用,或者它是否能够只阻塞重新分配并且安全,如果是这样,这样做是否足够聪明。

4

5 回答 5

3

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

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

于 2012-07-17T14:05:52.657 回答
1

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

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

synchronized(ModelUpdate.class) {
        monthArray = tempArray;
}
于 2012-07-17T14:17:20.693 回答
1

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

于 2012-07-17T14:18:37.777 回答
1

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

此外,JVM 可能会优化您的更清洁(第二)版本的代码,使其像第一个版本一样执行。因此,如果保留锁,最好坚持使用更清洁的版本。

于 2012-07-17T14:17:05.743 回答
1

这里一个明显的缺陷是您不应该对将要更改的对象进行同步。

我更喜欢第一个示例,而是使用普通锁

final Object lock = new Object();

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

然而,正如大多数人所说,声明 monthArrvolatile将产生相同的效果。

于 2012-07-17T14:19:35.903 回答