ReadWriteLock javadoc at Oracle和its implementation描述了锁的作用以及如何使用它,但没有说明是否使用volatile
关键字。
这与do-all-mutable-variables-need-to-be-volatile-when-using-locks不是同一个问题,因为我很高兴锁可以正确地同步访问和可见性,但volatile
对变量的使用仍然是一个好主意,例如对于编译器优化或任何其他原因?
我的缓存数据包含很少更改的List
和几个Maps
使用对象的各种属性映射列表中的对象。
private void reload() {
Set<Registration> newBeans = dao.listRegistered();
beans = Collections.unmodifiableSet(newBeans);
codeToBeanMap.clear();
userToBeanMap.clear();
nameToBeanMap.clear();
idToBeanMap.clear();
for (Registration bean : newBeans) {
codeToBeanMap.put(bean.getCode(), bean);
userToBeanMap.put(bean.getUser(), bean);
nameToBeanMap.put(bean.getName(), bean);
idToBeanMap.put(bean.getId(), bean);
}
}
什么是最好的声明?我有这个:
private Set<Registration> ecns;
private final Map<String, Registration> codeToBeanMap =
new HashMap<String, Registration>();
private final Map<String, Registration> userToBeanMap =
new HashMap<String, Registration>();
private final Map<String, Registration> nameToBeanMap =
new HashMap<String, Registration>();
private final Map<String, Registration> idToBeanMap =
new HashMap<String, Registration>();
答案 0 :(得分:1)
如果您要从锁定保护的代码中专门访问变量,那么将该变量指定为volatile
并且会产生一定的性能损失(它在纳秒内是多余的)将是多余的范围内)。
答案 1 :(得分:1)
这样的声明对并发性来说很好:
private final Map<String, Registration> idToBeanMap = new HashMap<>();
...因为Java保证声明final的字段将在其他线程可以访问它们之前初始化(至少Java 1.5以上)。不需要volatile关键字(无论如何都没有意义)。
但是,这并没有说明HashMap的内容 - 这不是线程安全的,并且不同的线程可能会看到不同或不一致的内容,除非您使用同步代码块。最简单的解决方案是使用ConcurrentHashMap。这将保证所有线程都能看到预期的Map内容。
然而,这只意味着地图上的操作将是原子的。在上面的代码中,您可能已经清除了一个映射而不是其他映射(例如),另一个线程尝试读取可能产生不一致结果的数据。
所以,因为看起来您的代码需要多个映射保持同步并且彼此一致,唯一的方法是使您的重载方法同步,并使客户端通过同一对象上的同步方法读取数据
答案 2 :(得分:0)
但是对于变量使用volatile仍然是一个好主意,例如对于编译器优化或任何其他原因?
如果任何volatile会阻止编译器优化而不是启用它们。
Oracle的ReadWriteLock javadoc及其实现描述了锁的作用以及如何使用它,但没有说明是否使用volatile关键字。
事实上,如果你看一下读写锁实现的接口,特别是Lock interface表明实现必须提供与synchronized()
块相同的内存可见性保证:
所有Lock实现必须强制执行内置监视器锁提供的相同内存同步语义,如Java™语言规范的第17.4节所述:
- 成功的锁定操作与成功的锁定操作具有相同的内存同步效果。
- 成功解锁操作具有与成功解锁操作相同的内存同步效果。