Map的putIfAbsent实现中的冗余分配

Joh*_*ane 2 java

看一下putIfAbsent界面中默认方法的实现Map,

default V putIfAbsent(K key, V value) {
    V v = get(key);
    if (v == null) {
        v = put(key, value);
    }

    return v;
}
Run Code Online (Sandbox Code Playgroud)

我想知道为什么这个任务

v = put(key, value);
Run Code Online (Sandbox Code Playgroud)

在那里完成而不是简单地丢弃返回的值?这个赋值似乎是不必要的,因为v已经存在null,put根据它的契约,这个方法在这种情况下总是返回.

Ste*_*n C 5

实现是这样的,因为javadoc声明它应该是:

实施要求:

对于此映射,默认实现等效于:

 V v = map.get(key);
 if (v == null)
     v = map.put(key, value);

 return v;
Run Code Online (Sandbox Code Playgroud)

但为什么这样指定呢?可能是拿在竞争条件的存在是合理的明确的行为不能得到解决1.

首先,这是javadoc所说的内容.

默认实现不保证此方法的同步或原子性属性.提供原子性保证的任何实现都必须覆盖此方法并记录其并发属性.

那么上面的实现将如何表现呢?

  1. 如果没有与另一个线程改变该映射条目的竞争,它将返回null或前一个值(不更新映射).

  2. 如果有一场比赛,另一个线程在通话结束后立即更新该条目get,则该map.put通话将不会返回null.相反,它实际上将返回另一个线程插入的值.该值将返回给调用者.

请注意,这与该putIfAbsent方法的主要描述并不完全一致.但这可以通过"无保证"声明来解释.而且,这有点道理.

这有用吗?嗯...可能是的,如果实际的Map方法是线程安全的.因为,如果调用代码想要查看,这将允许它检测到已经发生了竞赛,并且(如果在整个应用程序设计的上下文中有意义的话)尝试对其执行某些操作.

1 - 无法在不了解实现类行为的默认方法中解决竞争条件.例如,可以通过覆盖方法来获得原子属性,从而在实现类本身中解决它们.Mapjavadocs中的方法规范清楚地标明了这种可能性.


但最重要的是,所谓的冗余分配是存在的,因为规范清楚地表明它应该在那里.