Java Map'中的冗余分配;s的putIfAbsent实现
查看界面Java Map'中的冗余分配;s的putIfAbsent实现,java,Java,查看界面Map中默认方法putIfAbsent的实现 default V putIfAbsent(K key, V value) { V v = get(key); if (v == null) { v = put(key, value); } return v; } 我想知道为什么要做这个作业 v = put(key, value); 是在那里完成的,而不是简单地丢弃返回值?这个赋值似乎没有必要,因为v已经是null,这就是put方法根据其
Map
中默认方法putIfAbsent
的实现
default V putIfAbsent(K key, V value) {
V v = get(key);
if (v == null) {
v = put(key, value);
}
return v;
}
我想知道为什么要做这个作业
v = put(key, value);
是在那里完成的,而不是简单地丢弃返回值?这个赋值似乎没有必要,因为
v
已经是null
,这就是put
方法根据其约定在这种情况下始终返回的值。它必须始终返回旧值
如果映射由多个线程访问,则put
返回的值可能与get
返回的值不同,因此分配给v
可以确保更好的行为
当然,代码不适合处理多线程访问,因此任何有效的线程安全实现都会覆盖该方法以使其更好,但这是通用默认实现所能达到的最佳效果。实现是这样的,因为它的状态应该是: 实施要求: 对于此映射,默认实现相当于:
V v = map.get(key);
if (v == null)
v = map.put(key, value);
return v;
但为什么会这样规定呢?可能是为了在存在无法解决的竞争条件的情况下获得合理定义的行为1
首先,这里是javadoc对它的描述
默认实现不保证此方法的同步性或原子性属性。任何提供原子性保证的实现都必须重写此方法并记录其并发属性
那么,上述实现将如何运行
null
或上一个值(不更新映射)get
调用之后有一个争用且另一个线程立即更新条目,则map.put
调用将不会返回null
。相反,它将实际返回另一个线程插入的值。并且该值将返回给调用者putIfAbsent
方法的主要描述不完全一致。但“不保证”的声明却为这一点开脱。而且,这有点道理
这有用吗?好。。。如果实际的Map
方法是线程安全的,则可能是。因为,如果调用代码想要查看,这将允许它检测到已发生竞争,并(如果在整个应用程序设计的上下文中有意义的话)尝试对此采取一些措施
1-无法在不了解实现类行为的默认方法中解决争用条件。它们可以在实现类本身中解决。。。例如,通过重写该方法使其具有原子属性。而Map
javadocs中的方法规范清楚地标记了这种可能性
但归根结底,这种所谓的冗余分配是存在的,因为规范明确指出它应该存在。我的猜测是,它解释了可能存在的竞争条件。此方法不是原子操作。在
get
和put
调用之间,键值关联可能已经更新。我考虑的是并发而不是警告,但是putIfAbsent
文档中的短语“默认实现不保证此方法的同步性或原子性属性”表明情况并非如此。也许这是为了安全起见,正如回答中提到的。IMO这是一个糟糕的执行决定。如果put
中的值不同于get
,则该方法仍会更新键值对,而且返回值为非null,这通常表示put
实际上没有发生。这不是实现决策。这是一个规格决定。看看我的答案。改变行v=put(key,value)不是更好的设计决策吗
至if(put(key,value)!=null)抛出新的ConcurrentModificationException()代码>?不。并发集合不会引发该异常。此外,正如我所解释的,指定的行为是可预测和有用的。但如果设计得更好的话,它真的不会改变任何争论。