Reputation: 761
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
Upvotes: 2
Views: 7488
Reputation: 1075
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
Upvotes: 0
Reputation: 1507
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.
Upvotes: 3