Reputation: 3592
This is my loading cache definition:
private class ProductValue {
private long regionAValue;
private long regionBValue;
// constructor and general stuff here
}
private final LoadingCache<ProductId, ProductValue> productCache = CacheBuilder.newBuilder()
.expireAfterAccess(4, TimeUnit.MINUTES)
.build(new CacheLoader<ProductId, ProductValue>() {
@Override
public ProductValue load(final ProductId productId) throws Exception {
return updateProductValues(productId);
}
});
private ProductValue updateProductValues(final ProductId productId) {
// Read from disk and return
}
Now, I've a use case where I'm required to set the value of regionA or regionB in the cache until the next update happens. I'm utterly confused about the concurrency implications of the logic I've:
public void setProductValue(final ProductId productId, final boolean isTypeA, final long newValue) throws ExecutionException {
ProductValue existingValues = productCache.get(productId); // 1
if (isTypeA) {
existingValues.regionAValue = newValue;
} else {
existingValues.regionBValue = newValue;
}
productCache.put(productId, existingValues); // 2
}
In 1
I just read the reference of information stored in cache for given key, this get is thread safe because loading cache acts like a concurrent map. But between 1
and 2
this reference can be overwritten by some other thread. Since I've overwritten 'value' using reference which already existed in the cache, do I need to put the key-value pair in the cache? Do I need line 2
?
Upvotes: 0
Views: 5079
Reputation: 5723
(Disclaimer: I am not a Guava Cache expert)
I think you have two concurrency issues in your code:
existingValues.regionAValue = ...
and existingValues.setRegionValue(...)
. Other threads can see the state when only one operation is applied. I think that is not wanted. (correct?)get()
and the put()
the value may be loaded again in the cache and put()
overwrites a new value.Regarding 1:
If you have a more reads to the object then writes, a good option is to use an immutable object. You don't touch the instance but do a copy of the original object, mutate, and put the new object into the cache. This way only the final state becomes visible.
Regarding 2:
Atomic CAS operations can help you here (e.g. JSR107 compatible caches). The useful method would be boolean replace(K key, V oldValue, V newValue);
In Google Guava the CAS methods are accessible via the ConcurrentMap interface, that you can retrieve via asMap().
Upvotes: 2