使用Java ReadWriteLock同步缓存数据 - 是否将状态变量标记为volatile?

时间:2015-02-03 11:07:54

标签: java multithreading concurrency reentrantreadwritelock readwritelock

ReadWriteLock javadoc at Oracleits 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>();

3 个答案:

答案 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节所述:

     
      
  • 成功的锁定操作与成功的锁定操作具有相同的内存同步效果。
  •   
  • 成功解锁操作具有与成功解锁操作相同的内存同步效果。
  •