【问题标题】:ConcurrentHashMap as cacheConcurrentHashMap 作为缓存
【发布时间】:2013-08-21 14:36:01
【问题描述】:

如果使用 ConcurrentHashMap 作为映射,我会问自己实现线程安全的正确方法是什么?

在一本书中我发现了这样的内容:

private ConcurrentHashMap<KEY, VALUE> cache = new ConcurrentHashMap<>();

public V put(KEY key, VALUE value) {
    VALUE ret = cache.get(key);
    if (ret == null) {
        ret = cache.putIfAbsent(key, value);
        if (ret == null) {
            ret = value;
        }
    }
    return ret;
}

现在我问自己是否有必要像这样使 get 和可能的 put 原子化:

public V put(KEY key, VALUE value) {
    synchronized(cache) {
        VALUE ret = cache.get(key);
        if (ret == null) {
            ret = cache.putIfAbsent(key, value);
            if (ret == null) {
                ret = value;
            }
        }
    }
    return ret;
}

因为当 cache.get() 返回 null 时,另一个线程可能会使第一个线程的 cache.get() 结果无效?

干杯 奥利弗

【问题讨论】:

  • 那么你的问题到底是什么?
  • 我的问题是是否有必要将调用 cache.get(...) 和 cache.putIfAbsent(...) 放在同步块中并使用缓存监视器。
  • 不,没有必要。 putIfAbsent 为您处理原子性(正如它在 javadoc 中所述)。

标签: java caching thread-safety concurrenthashmap


【解决方案1】:

没必要。

下面的代码确实不是线程安全的,因为cache.get() 可以被另一个线程无效。

VALUE ret = cache.get(key);
if (ret == null) {...}

但是,代码只是为了优化(原子操作更昂贵)。原子性由map.putIfAbsent() 确保,它是原子的,因此是线程安全的。然而,如果cache.get() 返回其他东西然后null,则不会执行昂贵的原子操作。

【讨论】:

    【解决方案2】:

    您可以在同步块中应用延迟初始化。

        private static volatile ConcurrentHashMap<KEY, VALUE> cache = null;
    
        public static ConcurrentHashMap<KEY, VALUE> getCacheInstance() {
            if (cache == null) {
                synchronized(cache) {
                    if (cache == null) {
                        cache = new ConcurrentHashMap<>();
                    }
                }
            }
            return cache ;
        }
    
        public static put(ConcurrentHashMap<KEY, VALUE> cache) {
            VALUE ret = cache.get(KEY);
            if (ret == null) {...
            }     
        }
    

    在 Java 8 中实现本地线程安全缓存非常容易。

        private static final ConcurrentMap<KEY, VALUE> cache = 
                                              new ConcurrentHashMap();
    
        public Object get(String KEY) {
           return cache.computeIfAbsent(KEY, k - > createObject(k));
        }
    

    您可以在 Java 7 中通过双重检查线程安全来实现它。

        public Object get(String KEY) {
            if (!cache.containsKey(KEY)) {
                synchronized (cache) {
                   if (!cache.containsKey(KEY)) {
                       cache.put(KEY, createObject(KEY));
                   }
                }
            }
            return cache.get(KEY);
        }
    

    https://developer-should-know.com/post/116472734172/local-thread-safe-cache-with-java

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-21
      • 1970-01-01
      相关资源
      最近更新 更多