In my application, I'd like to have a cache that will be updated daily by an expensive operation (for example, fetching from remote and computing locally). The idea is every day, it will fetch and compute the latest cache for today. And at any time of today, any thread should be able to read from/write to the daily cache. And it's fine to serve the old data when the expensive operation is running daily and should not block any requests at any time.
I have written a simple code to illustrate the idea but not sure if it's the best practice or even correct in terms of multithreading. For example,
volatile required?Any suggestion will be much appreciated!
public class DailyCache {
private volatile ConcurrentHashMap<String, String> cache;
public DailyCache() {
cache = expensiveCalculation();
Executors.newScheduledThreadPool(1)
.scheduleAtFixedRate(() -> cache = expensiveCalculation(), 1, 1, TimeUnit.DAYS);
}
public String get(String key) {
return cache.get(key);
}
public void put(String key, String value) {
cache.put(key, value);
}
public ConcurrentHashMap<String, String> expensiveCalculation() {
// an expensive operation to fetch the cache for today
}
}
To answer your questions:
Q: is it "the best practice"
There are No Best Practices. If you haven't done so already, take the time to read that.
This question is unanswerable ... and not even meaningful.
Hint: it is time to remove "best practice" from your vocabulary ... and start questioning the wisdom of people who tell you that something is "best practice".
Q: is
volatilerequired?
Maybe. It depends on whether it is possible for the reference in cache to change.
volatile is required.volatile may not be required. But in that case you should declare the variable as final. If you do that, then the JLS guarantees that all threads will see the correct value for variable.In your code, it looks like you are periodically assigning a new value to cache. If so, then it needs to be volatile, or you need some other way to ensure that all worker threads see the updated cache value. (There are other ways ... but this is starting to smell of premature optimization.)
Q: What will happen if there is a cache reassignment finished in the middle of a get or put.
If the get or put call starts before the assignment, then they will definitely operate on the old cache. If not, it is not it will depend on whether the fetch of the cache occurs before or after the assignment. That is unpredictable.
There is one other thing that you don't seem to have considered. You say:
And it's fine to serve the old data when the expensive operation is running daily and should not block any requests at any time.
But you have not said if it is OK (or not) for the old cache to be updated while the new cache is being built. For example, consider this sequence of events:
Now we have the application running with the new cache, but the new cache contains a value1 for key1 that is older than the most recently used value (value2).
If that breaks your application, then you need a way to solve it. There will be ways ... but it will depend on aspects of your application logic that you haven't mentioned. For example, the scenarios in which regular application threads will do cache put operations.