我的getStatement方法线程是否安全?

时间:2016-11-24 21:51:03

标签: java multithreading thread-safety guava concurrenthashmap

我有一个下面的Singleton类,在我的getStatement方法中,我通过检查来填充CHM。

public class CacheHolder {
  private static final Map<String, PreparedStatement> holder = new ConcurrentHashMap<>();

  private static class Holder {
    private static final CacheHolder INSTANCE = new CacheHolder();
  }

  public static CacheHolder getInstance() {
    return Holder.INSTANCE;
  }

  private CacheHolder() {}

  public BoundStatement getStatement(String cql) {
    Session session = TestUtils.getInstance().getSession();
    PreparedStatement ps = holder.get(cql);
    if (ps == null) {
      ps = session.prepare(cql);
      holder.put(cql, ps);
    }
    return ps.bind();
  }
}

我的getStatement方法线程是否安全?

2 个答案:

答案 0 :(得分:2)

@javaguy提供的答案是正确的,但只是一个小优化,以确保在不需要时为每个线程执行同步块。

public static BoundStatement getStatement(String cql) {
    PreparedStatement ps = null;
    Session session = null;
    try {
      session = TestUtils.getInstance().getSession();
      PreparedStatement ps = holder.get(cql);
      if(ps == null) { // If PS is already present in cache, then we don't have to synchronize and make threads wait.
        synchronized {
          ps = holder.get(cql);
          if (ps == null) {
            ps = session.prepare(cql);
            holder.put(cql, ps);
          }
        }
      }
    } finally {
        //release the resources
    }
    return ps.bind();
  }

您也可以使用Guava Cache,或者如果您想要地图Guava MapMaker

使用番石榴缓存:

LoadingCache<String, PreparedStatement> cache = CacheBuilder.newBuilder()
       .maximumSize(1000)
       .expireAfterWrite(10, TimeUnit.MINUTES)
       .build(
           new CacheLoader<String, PreparedStatement>() {
             public PreparedStatement load(String cql) throws Exception {
               return createPreparedStatement(cql);
             }
           });

使用地图制作工具:

ConcurrentMap<String, PreparedStatement> cache = new MapMaker()
       .concurrencyLevel(32)
       .weakValues()
       .makeComputingMap(
           new Function<String, PreparedStatement>() {
             public PreparedStatement apply(String cql) {
               return createPreparedStatement(cql);
             }
           });

另外,我建议不要缓存PreparedStatement&#39; ,因为这些资源需要发布 AFAIK。

答案 1 :(得分:1)

请参阅@Bandi Kishore的答案,因为它比下面的答案更有效(对于synchronization的每次调用,以下答案都需要getStatement(),可以通过再添加一个{{1}来避免检查)。

  

我的getStatement方法线程是否安全?

不,它不是线程安全的,在您的null方法中,您正在进行 getStatement(String cql)检查竞争条件,这通常被称为双重检查锁定,您可以查看here。即,当线程正在执行null时,代码中存在竞争条件,您需要holder.get(cql);该代码的关键部分,如下所示:

synchronize

另外,作为旁注,请确保您正在释放资源。