Search code examples
javamultithreadingthread-safetyguavaconcurrenthashmap

How does "putIfAbsent" works in CHM?


I am working with Cassandra and using Datastax Java driver. I am trying to reuse prepared statements by caching it.

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

  public BoundStatement getStatement(String cql) {
    Session session = TestUtils.getInstance().getSession();
    PreparedStatement ps = holder.get(cql);
    // no statement is cached, create one and cache it now.
    if (ps == null) {
      holder.putIfAbsent(cql, session.prepare(cql));
    }
    return ps.bind();
  }

Prepared Statement and BoundStatement of datastax java driver.

This getStatement method will be called by multiple threads so I have to make sure it is thread safe. I am working with Java 7.

What will putIfAbsent do here if we get two same cql prepared statements? Is my code thread safe and there is no race condition?

Update:-

  public BoundStatement getStatement(String cql) {
    Session session = TestUtils.getInstance().getSession();
    PreparedStatement ps = holder.get(cql);
    // no statement is cached, create one and cache it now.
    if (ps == null) {
      synchronized (this) {
        ps = holder.get(cql);
        if (ps == null) {
          ps = session.prepare(cql);
          holder.put(cql, ps);
        }
      }
    }
    return ps.bind();
  }

Solution

  • Your code has a race condition which can result in session.prepare(cql) being called twice (or more) for any cql parameter. The putIfAbsent doesn't really offer any advantage over a normal put in this case.

    If you were on Java 8, you could write this efficiently without creating duplicates with

    PreparedStatement ps = holder.computeIfAbsent(cql, key -> session.prepare(key));