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

时间:2016-11-27 17:33:54

标签: java multithreading thread-safety guava atomicity

我的下方法线程是否安全?这个方法是Singleton类。

  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 StatementBoundStatement

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

2 个答案:

答案 0 :(得分:2)

由于.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仍然在内部使用相同的同步。

答案 1 :(得分:1)

如果您正在寻找某种形式的memoization,那么这是您在Java 7中可以做的最好/最简单.Guava有一个可以使用的计算Cache实现,而Java 8有computeIfAbsent Map界面中的方法,但你在这里显然是运气不好。

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

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