正在同步对类字段的访问



假设类中有一个字段很少更改/写入,但同时从中读取,是否有必要用ReadWriteLock保护它?

@Component
public class SomeClass {
private Map<String, String> someField;
@EventListener(ApplicationReadyEvent.class)
@Scheduled(fixedRate = 15, timeUnit = TimeUnit.MINUTES)
public void someScheduledActivity() {
// replace the whole map (A)
someField = theNewValue();
}
public String read(String id) {
// reading from the map field (B)
return this.someField.getOrDefault(id, null);
}
}

据我所知,我们必须在(A(处写锁,在(B(处读锁。但我被告知Java不同,不需要这种同步(或任何类型的同步(。这听起来很不一样,出乎我的意料。

是否有任何官方Java文档/指南可以帮助澄清这一点?

只有当操作是原子操作时才需要同步(请参阅https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html)。

您应该声明您的变量volatile,以确保您读取了最新的写入。

您不必使用ReadWriteLock,但必须确保对该字段的更新对其他线程可见。

您需要使映射字段不稳定,以防止内存一致性错误。仅仅是原子字段是不够的,将volatile放在上面告诉JVM这个字段正在被其他线程读取,因此需要使对它的写入可见。

保存引用的字段的可见性和该引用内容的可见性是两个独立的问题。

如果你的地图在结构上没有改变,那么在没有同步的情况下访问它是安全的。这意味着不插入或删除条目。关于结构修改的警告在map实现的api文档中,例如:https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/HashMap.html

未更新的安全发布的映射在读取时不需要确保内存可见性。但最好使用一个不可变的映射来实现这一点。

这方面最好的参考是《实践中的Java并发》一书。

TLDR:在实例成员声明中使用volatile。确保写入该字段的映射是不可变的。

字段同时更新时可能会出现错误。但someScheduledActivity方法中的更新默认情况下是序列化的,因此它是线程安全的,不需要同步。正如@Edgar Domingues所说,声明变量volatile是个好主意。

最新更新