如果将ConcurrentHashMap用作map我问自己,实现线程安全的正确方法是什么?
在一本书中我发现了这样的东西:
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和可能的原子像这样:
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()结果无效?
干杯 奥利弗
答案 0 :(得分:3)
没有必要。
以下代码由于cache.get()
而导致的线程不安全,可能会被另一个线程无效。
VALUE ret = cache.get(key);
if (ret == null) {...}
但是,代码仅用于优化(原子操作更昂贵)。原子性由map.putIfAbsent()
确保,它是原子的,因此是线程安全的。然而,如果cache.get()
返回其他内容然后null
,那么昂贵的原子操作就不会执行。
答案 1 :(得分:0)
您可以在同步块中使用延迟初始化。
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