java - 以线程安全的方式缓存preparestatement?

标签 java multithreading cassandra thread-safety datastax-java-driver

我正在缓存准备好的语句,以便在使用datastax java驱动程序(cassandra)时不必再次准备它。以下是我的代码,它可以工作:

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

  public ResultSetFuture send(final String cql, final Object... values) {
    return executeWithSession(new SessionCallable<ResultSetFuture>() {
      @Override
      public ResultSetFuture executeWithSession(Session session) {
        BoundStatement bs = getStatement(cql, values);
        bs.setConsistencyLevel(consistencyLevel);
        return session.executeAsync(bs);
      }
    });
  }

  private BoundStatement getStatement(final String cql, final Object... values) {
    Session session = getSession();
    PreparedStatement ps = cache.get(cql);
    // no statement cached, create one and cache it now.
    // below line is causing thread safety issue..
    if (ps == null) {
      ps = session.prepare(cql);
      PreparedStatement old = cache.putIfAbsent(cql, ps);
      if (old != null)
        ps = old;
    }
    return ps.bind(values);
  }


但是问题是send方法将被多个线程调用,因此我怀疑我的getStatement方法由于if (ps == null)检查而不是线程安全的。如何使它成为线程安全的?

我想避免使用synchronize关键字,所以想看看是否有更好的方法。到目前为止,我正在使用Java 7。

最佳答案

您可以改用computeIfAbsent。根据文档,它是:


  如果指定的键尚未与值关联,则尝试使用给定的映射函数计算其值,除非为null,否则将其输入此映射。整个方法调用是原子执行的,因此每个键最多可应用一次该功能。在进行计算时,可能会阻止其他线程在此映射上进行的某些尝试的更新操作,因此计算应简短而简单,并且不得尝试更新此映射的任何其他映射。


该代码如下所示:

private BoundStatement getStatement(final String cql, final Object... values) {
   PreparedStatement pr = cache.computeIfAbsent(
        query,  key -> session.prepare(key));
   return pr.bind(values);
}


虽然,因为准备工作可能需要更长的时间,所以不建议...我会改用显式锁...

附言请注意,Java driver 4.x has built-in cache,因此可以多次准备相同的语句。

关于java - 以线程安全的方式缓存preparestatement?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60329855/

相关文章:

java - 延迟setText标签java的问题

c++ - QT多线程和创建后移动对象线程

Cassandra 错误: expected 8 or 0 byte long for date

c++ - QNX 上的 std::call_once 延迟初始化问题

java - Android - 从默认图库应用程序中的可绘制对象中打开图像

java - 如何使用 jpa 执行 native memsql 查询

java - 使用列名从 ResultSet 获取小写列

java - 关于DSE检索的查询

c++ - 我如何知道 C++ 驱动程序是否丢失了与 Cassandra 的连接?

java - ant构建脚本将参数传递给java任务