Java 仅当映射值不存在时才创建并放置映射值,并获取它:线程安全实现

Java 仅当映射值不存在时才创建并放置映射值,并获取它:线程安全实现,java,multithreading,map,Java,Multithreading,Map,使此代码段线程安全的最佳方法是什么 私有静态最终映射 我还有什么其他的解决办法 我知道这在网络上是一个非常常见的话题,但我还没有找到一个清晰、完整和有效的示例。使用ConcurrentHashMap和您使用的lazy init模式 public static B putIfNeededAndGet(A key) { B value = map.get(key); if (value == null) { value = buildB(...); B

使此代码段线程安全的最佳方法是什么

私有静态最终映射
我还有什么其他的解决办法


我知道这在网络上是一个非常常见的话题,但我还没有找到一个清晰、完整和有效的示例。

使用ConcurrentHashMap和您使用的lazy init模式

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;
}

这可能不是您要寻找的答案,但使用,它已经完成了所有这些以及更多功能:

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);
         }
       });
private static final LoadingCache CACHE=CacheBuilder.newBuilder()
.最大尺寸(100)//如有必要
.建造(
新缓存加载程序(){
公共B加载(A键){
返回buildB(key);
}
});
您还可以轻松添加定时过期和其他功能


此缓存将确保
load()
(或者在您的情况下
buildB
)不会与相同的
键同时调用。如果一个线程已经在构建一个
B
,那么任何其他调用程序都将等待该线程。

在上述解决方案中,可能有许多线程将同时对
进程B(…)
进行分类,因此所有线程都将进行计算。但是在我的例子中,我使用的是
Future
,一个线程只获取旧值
null
,因此它将只计算
进程b
,其余线程将等待
f.get()

private静态最终ConcurrentMap=new ConcurrentHashMap();
公共静态B putifNeedAndGet(A键){
while(true){
Future f=map.get(键);
如果(f==null){
Callable eval=新的Callable(){
public B call()抛出InterruptedException{
返回buildB(…);
}
};
FutureTask ft=新的FutureTask(eval);
f=地图上的putIfAbsent(arg,ft);
如果(f==null){
f=英尺;
ft.run();
}
}
试一试{
返回f.get();
}捕获(取消异常e){
cache.remove(arg,f);
}捕获(执行例外){
}
}
}

认为这可能对其他人也有用,我使用java 8 lambdas创建了这个函数,它对我来说非常有用:

private <T> T getOrCreate(Object key, Map<Object, T> map,
                                Function<Object, T> creationFunction) {
    T value = map.get(key);

    // if the doesn't exist yet - create and add it
    if (value == null) {
        value = creationFunction.apply(key);
        map.put(label, metric);
    }
    return value;
}
我创建它是为了一些特定的东西,但将上下文和代码更改为更一般的外观,这就是为什么我的creationFunction有一个参数,也可以没有参数

您还可以通过将对象更改为泛型类型来对其进行更多的泛型化,如果不清楚,请告诉我,我将添加另一个示例

更新:

我刚刚发现Map.computeIfAbsent基本上也是这样,我一定很喜欢java 8:)

您最关心的是什么?那
buildB()
可能会被调用两次吗?@AndrewLogvinov是的。好吧,这可能发生,但尽可能少;)+1.我不会过度设计,保持简单。在这个场景中,最糟糕的事情是
buildB()
会被调用多次。+1-这个解决方案处理了这个场景中的两个问题-线程安全和一次性构建。这正是我需要的,因为我计划使用WeakHashMap。谢谢;)
private <T> T getOrCreate(Object key, Map<Object, T> map,
                                Function<Object, T> creationFunction) {
    T value = map.get(key);

    // if the doesn't exist yet - create and add it
    if (value == null) {
        value = creationFunction.apply(key);
        map.put(label, metric);
    }
    return value;
}
Object o = getOrCreate(key, map, s -> createSpecialObjectWithKey(key));