Last active
September 7, 2018 19:46
-
-
Save theotherian/7478882 to your computer and use it in GitHub Desktop.
Blocking on cache reads beyond the first is just plain rude in my opinion, so refresh in the background.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Blocking on cache reads beyond the first is just plain rude in my opinion... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.theotherian.cache; | |
import java.util.concurrent.Callable; | |
import java.util.concurrent.ExecutorService; | |
import java.util.concurrent.Executors; | |
import java.util.concurrent.ThreadFactory; | |
import java.util.concurrent.TimeUnit; | |
import java.util.concurrent.atomic.AtomicInteger; | |
import com.google.common.cache.CacheBuilder; | |
import com.google.common.cache.CacheLoader; | |
import com.google.common.cache.LoadingCache; | |
import com.google.common.util.concurrent.ListenableFuture; | |
import com.google.common.util.concurrent.ListeningExecutorService; | |
import com.google.common.util.concurrent.MoreExecutors; | |
import com.google.common.util.concurrent.ThreadFactoryBuilder; | |
public class NonBlockingCache { | |
public static void main(String[] args) throws Exception { | |
final AtomicInteger value = new AtomicInteger(1); | |
// nicely named threads are nice | |
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("MyCacheRefresher-pool-%d").setDaemon(true).build(); | |
ExecutorService parentExecutor = Executors.newSingleThreadExecutor(threadFactory); | |
// create an executor that provide ListenableFuture instances | |
final ListeningExecutorService executorService = MoreExecutors.listeningDecorator(parentExecutor); | |
final LoadingCache<String, Integer> cache = CacheBuilder.newBuilder() | |
.maximumSize(1) | |
// the cache will try to refresh any entry after it's first written if it's accessed more than 10 seconds | |
// after the last write | |
.refreshAfterWrite(10, TimeUnit.SECONDS) | |
.build( | |
new CacheLoader<String, Integer>() { | |
public Integer load(String key) throws Exception { | |
Thread.sleep(5000); | |
System.out.println("RELOADING!"); | |
return value.getAndIncrement(); | |
} | |
@Override | |
public ListenableFuture<Integer> reload(final String key, Integer oldValue) throws Exception { | |
// we need to load new values asynchronously, so that calls to read values from the cache don't block | |
ListenableFuture<Integer> listenableFuture = executorService.submit(new Callable<Integer>() { | |
@Override | |
public Integer call() throws Exception { | |
System.out.println("Async reload event"); | |
return load(key); | |
} | |
}); | |
return listenableFuture; | |
} | |
}); | |
for (int i = 0; i < 100; i++) { | |
long start = System.currentTimeMillis(); | |
Integer unchecked = cache.getUnchecked("foo"); | |
System.out.printf("Took %dms to load result %d: %d\n", (System.currentTimeMillis() - start), (i + 1), unchecked); | |
Thread.sleep(1000); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi! I found this gist extremely useful, thank you so much! It helped me a lot.
There's something I got curious about, though: why do you use a daemon thread here? Does it increase performance or is it just your own preference in threading?
I am new in threading. Thanx.