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



Oracle的ReadWriteLock javadoc及其实现描述了锁的作用以及如何使用它,但没有说明是否使用volatile关键字。

这与使用时需要易失性的可变变量不是同一个问题,因为我很高兴锁将正确同步访问和可见性,但是对变量使用 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>();

如果您要仅从由锁保护的代码访问变量,那么将该变量指定为 volatile 是多余的,并且会导致一定的性能影响(它在纳秒范围内)。

像这样的声明适用于并发:

private final Map<String, Registration> idToBeanMap = new HashMap<>();

。因为 Java 保证声明为 final 的字段将在其他线程访问它们之前进行初始化(至少从 Java 1.5 开始)。不需要 volatile 关键字(无论如何,对于 final,它没有任何意义)。

但是,这并没有说明 HashMap 的内容 - 这不是线程安全的,除非您使用同步的代码块,否则不同的线程可能会看到不同或不一致的内容。最简单的解决方案是改用ConcurrentHashMap。这将保证所有线程都能看到预期的 Map 内容。

但是,这仅意味着地图上的操作将是原子的。在上面的代码中,您可能已经清除了一个映射,但在另一个线程尝试读取可能给出不一致结果的数据时没有清除其他映射(例如)。

因此,由于您的代码看起来需要多个 Maps 保持同步并彼此一致,因此唯一的方法是使您的重新加载方法同步,并使读取数据的客户端通过同一对象的同步方法执行此操作。

但是,对变量使用易失性仍然是一个好主意,例如用于编译器优化或任何其他原因?

如果有任何不稳定因素会阻止编译器优化,而不是启用它们。

Oracle 的 ReadWriteLock javadoc 及其实现描述了锁的作用以及如何使用它,但没有说明是否使用 volatile 关键字。

事实上,如果您查看读写锁实现的接口,特别是 Lock 接口声明实现必须提供与synchronized()块相同的内存可见性保证:

所有 Lock 实现都必须强制执行内置监视器锁提供的相同内存同步语义,如 Java™ 语言规范的第 17.4 节所述:

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

最新更新