문제

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

도움이 되었습니까?

해결책

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.

다른 팁

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

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top