我有一个具有缓存(类型为Cache)的Runnable,我们假定它提供了线程安全的操作。此Runnable对象由多个线程使用。
我们的线程从外部源获取对象,然后
我正在寻找正确的方案(即最少的synchronized
代码)来可靠地使用缓存。
我想出了以下方案:
MyObject current = cache.getIfPresent(givenKey);
if (current == null) {
MyObject prev = cache.asMap().putIfAbsent(givenKey, givenObj);
if (prev == null) {
// successful put in cache
return givenObj;
}
}
// current != null or another thread update
synchronized (current) {
return update(current, givenObj); // in place change of current
}
我的计划背后的关键思想+可靠性的“证明”:
current
是null
,则由于缓存是线程安全的,因此恰好一个线程将能够将该对象放入缓存,而其他线程将看到prev != null
current
(要更新的对象)上进行同步。 问题
volatile
使内存同步可靠。我在这里需要它吗?谢谢!
答案 0 :(得分:1)
1)否您的架构不可靠 你不应该打电话
cache.asMap().putIfAbsent(givenKey, givenObj);
使用番石榴文档方法cache.get(K键,可调用加载程序)比使用asMap方法更好。
2)是的,可以对其进行优化 您应该改为调用此方法:
cache.get(K key, Callable<? extends V> loader)
如果该值已经在缓存中,则此方法将返回该值;如果该值不在缓存中,则它将从加载程序中将该值添加到缓存中并返回。
例如:
MyObject objInCache = cache.get(givenKey, ()->givenObj)
if(!objInCache.equals(givenobj)){
//obje was in the cache,
//update object
}
3)如果缓存是线程安全的,则不需要volatile
答案 1 :(得分:1)
- 检查对象密钥是否存在于缓存中
- 如果没有,那么放
- 如果它已经在缓存中,则进行更新
这可以使用Map
视图的compute方法来实现。
cache.asMap().compute(givenKey, (key, oldValue) -> {
return (oldValue == null)
? create(key)
: update(current, oldValue);
});
这是线程安全的,应允许并发计算不同的键。如果返回的新值是null
,那么将删除该映射,否则将建立/更新该映射。
不幸的是,这在Guava中并未进行优化,因为它在添加Java 8兼容性时已被搭建。您应该选择27.0.1
或更高版本,因为其中存在一些令人讨厌的错误。
Caffeine是为此功能而设计的Java 8重写。它基于从Guava(相似的界面,但重新审视了一些设计选择),现代算法研究和生态系统改进中获得的经验教训。两者都很出色,但是您可能会发现咖啡因更适合更高级的情况。