【发布时间】:2020-07-01 08:34:18
【问题描述】:
考虑以下代码:
ConcurrentHashMap<String, Value> map = new ConcurrentHashMap<>();
boolean foo(String key) {
Value value = map.get(key);
if (value == null) {
value = map.putIfAbsent(key, new Value());
if (value == null) {
// do some stuff
return true;
}
}
// do some other stuff
return false;
}
假设foo() 被多个线程同时调用。还假设调用new Value() 很昂贵。代码很冗长,仍然会导致创建冗余的Value 对象。上述逻辑能否以保证不创建冗余Value 对象的方式实现(即最多调用一次new Value())?我正在寻找一个干净的实现——无需显式获取锁的最少代码。
computeIfAbsent 可能是一个不错的选择,但是它的返回语义不符合所需的逻辑。
【问题讨论】:
-
不知道为什么你认为
Value value = map.computeIfAbsent(key, Value::new);不适用。 -
@assylias 好吧,值构造函数不带键。但是忽略参数的 lambda 并没有错。
computeIfAbsent(key, k -> new Value()) -
@assylias 我指的是
computeIfAbsent与putIfAbsent的返回语义。问题中的逻辑根据是否创建条目采用单独的代码路径。我们无法使用computeIfAbsent清楚地解决这个问题 -
听起来你有竞争条件。两个线程调用此方法,#1 创建并添加一个新对象,#2 获取新对象。如果 #2 先开始,它将表现得好像 #1 已经完成。根据您对我的其他评论的回复,也许您应该考虑使用“计算”,在这种情况下,您会获得旧值并且可以进行特殊分支。
-
@matt 不确定我是否理解竞态条件是如何发生的;
compute也不会公开是否创建了条目,因此需要此类输入的compute之外的代码(例如条件return语句)将不起作用。
标签: java