使此代码段具有线程安全性的最佳方法是什么?
private static final Map<A, B> MAP = new HashMap<A, B>();
public static B putIfNeededAndGet(A key) {
B value = MAP.get(key);
if (value == null) {
value = buildB(...);
MAP.put(key, value);
}
return value;
}
private static B buildB(...) {
// business, can be quite long
}
以下是我可以考虑的一些解决方案:
ConcurrentHashMap
,但是如果我很好理解,它只会使原子put
和get
操作具有线程安全性,即,它不能确保给定值的buildB()
方法仅被调用一次。 Collections.synchronizedMap(new HashMap<A, B>())
,但是我会遇到与第一点相同的问题。 putIfNeededAndGet()
方法设置为synchronized
,但是我真的可以有很多线程一起访问此方法,因此它可能会非常昂贵。 我还能有什么其他解决方案?
我知道这是Web上很常见的话题,但是我还没有找到一个清晰,完整且有效的示例。
最佳答案
这可能不是您要查找的答案,但是使用Guava CacheBuilder
,它已经完成了所有以及更多的工作:
private static final LoadingCache<A, B> CACHE = CacheBuilder.newBuilder()
.maximumSize(100) // if necessary
.build(
new CacheLoader<A, B>() {
public B load(A key) {
return buildB(key);
}
});
您还可以轻松地添加定时到期时间和其他功能。
此缓存将确保不会使用相同的
load()
并发调用buildB
(或者在您的情况下为key
)。如果一个线程已经在构建B
,则任何其他调用者都将等待该线程。