将 "get/check/put" 的使用替换为 putIfAbsent

Replace use of "get/check/put" with putIfAbsent

我正在使用 Cassandra 并使用 Datastax Java 驱动程序。我正在尝试通过缓存来重用准备好的语句。

  private static final Map<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) {
      synchronized (this) {
        ps = holder.get(cql);
        if (ps == null) {
          ps = session.prepare(cql);
          holder.put(cql, ps);
        }
      }
    }
    return ps.bind();
  }

我上面的 getStatement 方法将被多个线程调用,所以我必须确保它是线程安全的。我正在使用 Java 7,所以很遗憾不能使用 computeIfAbsent

当我 运行 我的代码针对静态分析工具时,它给了我这个小警告,这让我想在 Java 7 中有没有更好的方法来编写上面的代码?

Might be better to replace use of get/check/put with putIfAbsent

更新:

  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) {
      ps = session.prepare(cql);
      PreparedStatement old = holder.putIfAbsent(cql, ps);
      if (old!=null)
        ps=old;
    }
    return ps.bind();
  }

你拥有它的方式并不算太糟糕,除了一个线程可以阻塞另一个线程,即使它们没有尝试制作相同的准备好的语句。

在Java8中使用computeIfAbsent确实会好很多。在Java7中,你可以这样做:

ps = holder.get(cql);
if (ps == null) {
  ps = session.prepare(cql);
  PreparedStatement old = holder.putIfAbsent(cql, ps);
  if (old!=null)
    ps=old;
}

如果两个线程同时尝试创建相同的 PreparedStatement,您偶尔会创建一个不必要的 PreparedStatement,但在最坏的情况下,这相当于不使用缓存。

或者,如果您可以使用 guava 库,则 guava LoadingCache 会完全满足您的要求:https://google.github.io/guava/releases/16.0/api/docs/com/google/common/cache/CacheBuilder.html