john
john

Reputation: 11709

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();
  }

Upvotes: 0

Views: 323

Answers (2)

maaartinus
maaartinus

Reputation: 46482

You indeed do have a race condition, but your putIfAbsent is still probably slightly better than pure put, as it keeps the old statement, so there are never two instances in real use. The advantage is tiny as it manifests only in case of a race condition.

It looks like a version of a Double-checked locking. Just put a synchronized block around the initialization a recheck if it's really needed.

I'm rather sceptical about caching the PreparedStatement as it's mutable. You may need multiple instances for the same cql. This is doable, too, but you'd need to acquire and release them properly.

Upvotes: 1

Kayaman
Kayaman

Reputation: 73568

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));

Upvotes: 1

Related Questions