Domanda

if a ConcurrentHashMap is used as map I ask myself what is the correct way to achieve thread safety?

In a book I found someting like this:

private ConcurrentHashMap<KEY, VALUE> cache = new ConcurrentHashMap<>();

public V put(KEY key, VALUE value) {
    VALUE ret = cache.get(key);
    if (ret == null) {
        ret = cache.putIfAbsent(key, value);
        if (ret == null) {
            ret = value;
        }
    }
    return ret;
}

Now I ask myself isn't it necessary to make the the get and possible put atomic like this:

public V put(KEY key, VALUE value) {
    synchronized(cache) {
        VALUE ret = cache.get(key);
        if (ret == null) {
            ret = cache.putIfAbsent(key, value);
            if (ret == null) {
                ret = value;
            }
        }
    }
    return ret;
}

Because when cache.get() returns null, another thread could invalidate the cache.get() result for the 1st thread?

Cheers Oliver

È stato utile?

Soluzione

It is not necessary.

It is true that following code would not be thread-safe as a result of cache.get() can be invalidate by another thread.

VALUE ret = cache.get(key);
if (ret == null) {...}

However, the code is there just for an optimization (atomic operations are more expensive). Atomicity is ensured by map.putIfAbsent() which is atomic and therefore thread-safe. Nevertheless, if cache.get() returns something else then null, expensive atomic operation does not perform.

Altri suggerimenti

You can apply with lazy initialization with in synchronize block.

    private static volatile ConcurrentHashMap<KEY, VALUE> cache = null;

    public static ConcurrentHashMap<KEY, VALUE> getCacheInstance() {
        if (cache == null) {
            synchronized(cache) {
                if (cache == null) {
                    cache = new ConcurrentHashMap<>();
                }
            }
        }
        return cache ;
    }

    public static put(ConcurrentHashMap<KEY, VALUE> cache) {
        VALUE ret = cache.get(KEY);
        if (ret == null) {...
        }     
    }

In Java 8 implement local thread-safe cache is very easy.

    private static final ConcurrentMap<KEY, VALUE> cache = 
                                          new ConcurrentHashMap();

    public Object get(String KEY) {
       return cache.computeIfAbsent(KEY, k - > createObject(k));
    }

You can implement it in Java 7 with double checking thread safety.

    public Object get(String KEY) {
        if (!cache.containsKey(KEY)) {
            synchronized (cache) {
               if (!cache.containsKey(KEY)) {
                   cache.put(KEY, createObject(KEY));
               }
            }
        }
        return cache.get(KEY);
    }

https://developer-should-know.com/post/116472734172/local-thread-safe-cache-with-java

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top