Reputation:
I have a use case where I want to populate entries into a data structure from multiple threads and after a particular size is reached start dropping old records. So I decided to use Guava Loading Cache for this.
I want to populate entries into my Loading Cache from multiple threads and I am setting eviction based policy as Size Based Eviction
.
private final ScheduledExecutorService executorService = Executors
.newSingleThreadScheduledExecutor();
private final LoadingCache<String, DataBuilder> cache =
CacheBuilder.newBuilder().maximumSize(10000000)
.removalListener(RemovalListeners.asynchronous(new CustomListener(), executorService))
.build(new CacheLoader<String, DataBuilder>() {
@Override
public DataBuilder load(String key) throws Exception {
// what I should do here?
// return
}
});
// this will be called from multiple threads to populate the cache
public void addToCache(String key, DataBuilder dataBuilder) {
// what I should do here?
//cache.get(key).
}
My addToCache
method will be called from multiple threads to populate the cache
. I am confuse what I should be doing inside addToCache
method to fill the cache and also what does my load
method looks like?
Here DataBuilder
is my builder pattern.
Upvotes: 5
Views: 10214
Reputation: 44995
Obviously your problem is that you don't get the main purpose of a CacheLoader
.
A CacheLoader
is used to automatically load the value of a given key (which doesn't exist in the cache yet) when calling get(K key)
or getUnchecked(K key)
in way that even if we have several threads trying to get the value of the same key at the same time, only one thread will actually load the value and once done all calling threads will have the same value.
This is typically useful when the value to load takes some time, like for example when it is the result of a database access or a long computation, because the longer it takes the higher is the probability to have several threads trying to load the same value at the same time which would waste resources without a mechanism that ensures that only one thread will load the data for all calling threads.
So here let's say that your DataBuilder
's instances are long to build or you simply need to ensure that all threads will have the same instance for a given key, you would then indeed need a CacheLoader
and it would look like this:
new CacheLoader<String, DataBuilder>() {
@Override
public DataBuilder load(String key) throws Exception {
return callSomeMethodToBuildItFromTheKey(key); // could be new DataBuilder(key)
}
}
Thanks to the CacheLoader
, you have no need to call put
explicitly anymore as your cache will be populated behind the scene by the threads calling cache.get(myKey)
or cache.getUnchecked(myKey)
.
If you want to manually populate your cache, you can simply use the put(K key, V value)
method like any Cache
as next:
public void addToCache(String key, DataBuilder dataBuilder) {
cache.put(key, dataBuilder);
}
If you intend to populate the cache yourself, you don't need a CacheLoader
, you can simply call build()
instead of build(CacheLoader<? super K1,V1> loader)
to build your Cache
instance (it won't be a LoadingCache
anymore).
Your code would then be:
private final Cache<String, DataBuilder> cache =
CacheBuilder.newBuilder().maximumSize(10000000)
.removalListener(
RemovalListeners.asynchronous(new CustomListener(), executorService)
).build();
Upvotes: 13