In his talk about Effective Java at 54:15 Joshua Bloch recommends to use get
before putIfAbsent
in order to improve performance and concurrency. This leads me to the question why this optimization is already not build in like
public V BETTER_putIfAbsent(K key, V value) {
V result = get(key);
开发者_开发技巧 if (result!=null) return result;
return ORIGINAL_putIfAbsent(key, value);
}
I would guess that it is because performance depends on the usage pattern. For a map where most putIfAbsent calls would succeed, then guarding it with a get will be slower. For a map where putIfAbsent will often fail, then guarding it with a get will be faster.
By not making the optimization for the common failure case, you are free to choose which method is faster for your map.
In the example from the video, putIfAbsent will usually fail, so it is faster to guard it with a get.
This adds a double checked locking, the transactional semantics remains the same; so it is not wrong.
Whether it is actually an optimization depends on usage. We are always tempted to check for special cases that we know cheaper solutions exist
if A
cheapSolutionForA();
else
genericSolution();
This may work, or not - if A
is rarely true
, the additional check costs more than it saves. (and when A indeed is true on occasion, it can disrupt CPU branch prediction, it could have been cheaper to always go with the generic solution even when A=true)
In Joshua's example, A
indeed is true frequently. He must be requesting intern string for the same string(same in value, not in identity) many times, therefore in most calls the map already has the key.
If every call to intern()
gets a different string, then the map never has the key, and his optimization backfires - the "optimization" costs more time, and saves none.
Of course, when it comes to string intern, the 1st case is more realistic in practice .
In general though, putIfAbsent()
cannot predict how it's being used, so it's unwise to include this special case "optimization" inside it. In many use cases, contention is low, the map most likely doesn't have the key when putIfAbsent
is called, it would be wrong in these cases if the "optimzation" is built in.
精彩评论