Search code examples
javajava.util.concurrentconcurrenthashmap

Check failure of putIfAbsent


Is this a valid code to write,if I wish to avoid unnecessary contains call?
I wish to avoid a contains call on every invocation,as this is highly time sensitive code.

cancelretryCountMap.putIfAbsent(tag,new AtomicInteger(0));
count = cancelretryCountMap.get(tag).incrementAndGet();
if(count > 10){
     ///abort after x retries
     ....
}

I am using JDK 7


Solution

  • Usually, you would use putIfAbsent like this:

    final AtomicInteger present = map.get(tag);
    int count; 
    
    if (present != null) {
    
        count = present.incrementAndGet(); 
    
    } else {
    
        final AtomicInteger instance = new AtomicInteger(0);
        final AtomicInteger marker = map.putIfAbsent(tag, instance);
    
        if (marker == null) {
    
            count = instance.incrementAndGet();
    
        } else {
    
            count = marker.incrementAndGet();
        }
    }
    

    The reason for the explicit get being, that you want to avoid the allocation of the default value in the "happy" path (i.e., when there is already an entry with the given key).

    If there is no matching entry, you have to use the return value of putIfAbsent in order to distinguish between

    • the entry was still missing (and the default value has been added due to the call), in which case the method returns null, and
    • some other thread has won the race and inserted the new entry after the call to get (in which case the method returns the current value associated with the given key)

    You can abstract this sequence by introducing a helper method, e.g.,

    interface Supplier<T> {
        T get();
    } 
    
    static <T> T computeIfAbsent(ConcurrentMap<K,T> map, T key, Supplier<? extends T> producer) {
        final T present = map.get(key);
        if (present != null) {
            return present;
        } else {
            final T fallback = producer.get();
            final T marker = map.putIfAbsent(key, fallback);
            if (marker == null) {
                return fallback;
            } else {
                return marker;
            }
        }
    }
    

    You could use this in your example:

    static final Supplier<AtomicInteger> newAtomicInteger = new Supplier<AtomicInteger>() {
        public AtomicInteger get() { return new AtomicInteger(0); }
    };
    
    void yourMethodWhatever(Object tag) {
        final AtomicInteger counter = computeIfAbsent(cancelretryCountMap, tag, newAtomicInteger);
        if (counter.incrementAndGet() > 10) {
            ... whatever ...
        }
    }
    

    Note, that this is actually already provided in the JDK 8 as default method on Map, but since you are still on JDK 7, you have to roll your own, as is done here.