sp0*_*00m 8 java multithreading map
使这个片段线程安全的最佳方法是什么?
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
}
Run Code Online (Sandbox Code Playgroud)
以下是我能想到的几个解决方案:
ConcurrentHashMap
,但如果我很好理解,它只是使原子put
和get
操作线程安全,即它不能确保对buildB()
给定值只调用一次方法.Collections.synchronizedMap(new HashMap<A, B>())
,但我会遇到与第一点相同的问题.putIfNeededAndGet()
方法synchronized
,但我可以有很多线程一起访问这个方法,所以它可能非常昂贵.我可以提供哪些其他解决方案?
我知道这是一个很常见的网络主题,但我还没有找到一个清晰,完整和有效的例子.
使用ConcurrentHashMap和您使用的延迟初始化模式
public static B putIfNeededAndGet(A key) {
B value = map.get(key);
if (value == null) {
value = buildB(...);
B oldValue = map.putIfAbsent(key, value);
if (oldValue != null) {
value = oldValue;
}
}
return value;
}
Run Code Online (Sandbox Code Playgroud)
这可能不是您正在寻找的答案,但是使用 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);
}
});
Run Code Online (Sandbox Code Playgroud)
您还可以轻松添加定时过期和其他功能。
此缓存将确保load()
(或在您的情况下buildB
)不会与相同的同时调用key
。如果一个线程已经在构建 a B
,那么任何其他调用者将只等待该线程。