如何使用 get 和 put 作为原子操作使并发哈希映射线程安全?

How to make concurrent hash map thread safe with get and put as an atomic operation?

我下面的方法线程安全吗?此方法在 Singleton class.

  private static final Map<String, PreparedStatement> holder = new ConcurrentHashMap<>();

  public BoundStatement getStatement(String cql) {
    Session session = TestUtils.getInstance().getSession();
    PreparedStatement ps = holder.get(cql);
    if(ps == null) { // If "ps" is already present in cache, then we don't have to synchronize and make threads wait.
        synchronized {
          ps = holder.get(cql);
          if (ps == null) {
            ps = session.prepare(cql);
            holder.put(cql, ps);
          }
        }
    }
    return ps.bind();
  }

我正在使用 Cassandra 并使用 datastax java 驱动程序,所以我正在重用准备好的语句,这就是我将它缓存在这里的原因。 Prepared Statement and BoundStatement.

有没有更好的方法使我的 getStatement 方法线程安全(如果它是线程安全的)而不是像那样使用同步块?任何其他可能对此类操作线程安全的数据结构?我正在与 Java 7.

合作

因为.putIfAbsent在Java7中,你可以使用它:

 private static final ConcurrentHashMap<String, PreparedStatement> holder = new ConcurrentHashMap<>();

  public BoundStatement getStatement(String cql) {
    Session session = TestUtils.getInstance().getSession();
    PreparedStatement ps = holder.get(cql);
    if(ps == null) { // If "ps" is already present in cache, then we don't have to synchronize and make threads wait.

         if (holder.putIfAbsent(cql, session.prepare(cql)) != null) {
            // Someone else got there before, handle
         }
    }
    return ps.bind();
  }

请注意,putIfAbsent 仍然在内部使用相同的同步。

如果你想做某种形式的记忆,那么这就是你可以在 Java 中做的 best/easiest 7. Guava 有一个你可以使用的计算缓存实现 Java 8 在 Map 接口中有一个 computeIfAbsent 方法,但你显然在这里不走运。

如果您可以像 Alexey 的回答所建议的那样在空竞赛中创建对象,那将是最好的解决方案。如果不能,那么您的实现既线程安全又合理。

这是一种双重检查锁定的形式,但是,通过使用此实现,您可以使用 CHM 的 putget 方法确保先发生排序。