Reputation: 11709
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
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
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