考虑这个简单的程序:
import java.util.concurrent.ExecutionException;
import java.util.logging.Logger;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
public class Main {
final static Logger logger = Logger.getLogger(Main.class.getName());
public static void main(String[] args) throws ExecutionException, InterruptedException {
final LoadingCache<Integer, String> cache = CacheBuilder.newBuilder().build(
new CacheLoader<Integer, String>() {
@Override
public String load(Integer arg0) throws Exception {
logger.info("Cache builder START: " + arg0);
Thread.sleep(4000);
logger.info("Cache builder FINISH: " + arg0);
return "This is what CacheBuilder returned for key " + arg0;
}
});
Thread getterThread = new Getter(cache);
getterThread.start();
Thread setterThread = new Setter(cache);
setterThread.start();
getterThread.join();
setterThread.join();
logger.info("Finally in cache we have: " + cache.get(1));
}
private static final class Getter extends Thread {
private final LoadingCache<Integer, String> cache;
private Getter(LoadingCache<Integer, String> cache) {
this.cache = cache;
}
@Override
public void run() {
try {
logger.info("Getter thread reads 1st time " + cache.get(1)
+ " <<<<<<<<<< WHAT !?!");
// allow the setter to put the value
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("Getter thread reads 2nd time " + cache.get(1));
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
private static final class Setter extends Thread {
private final LoadingCache<Integer, String> cache;
private Setter(LoadingCache<Integer, String> cache) {
this.cache = cache;
}
@Override
public void run() {
try {
// deliberately wait to allow the Getter thread
// trigger cache loading
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
cache.put(1, "This isn't where I parked my car!");
logger.info("Setter thread now reads: " + cache.get(1));
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
输出是:
2013-11-08 15:24:32 INFO Main$1 load Cache builder START: 1
2013-11-08 15:24:32 INFO Main$Setter run Setter thread now reads: This isn't where I parked my car!
2013-11-08 15:24:36 INFO Main$1 load Cache builder FINISH: 1
2013-11-08 15:24:36 INFO Main$Getter run Getter thread reads 1st time This is what CacheBuilder returned for key 1 <<<<<<<<<< WHAT !?!
2013-11-08 15:24:37 INFO Main$Getter run Getter thread reads 2nd time This isn't where I parked my car!
2013-11-08 15:24:37 INFO Main main Finally in cache we have: This isn't where I parked my car!
我在Getter线程中得到了“这就是CacheBuilder为key 1返回的内容”。 显然这是因为Getter调用的get(1)触发了缓存加载,但同时Setter线程会为key 1添加一些其他值。 我希望它能像Setter那样返回原来的东西=“这不是我停车的地方!” (我第二次得到Getter检索1的值)。
我错过了什么吗?
提前致谢
答案 0 :(得分:0)
是。缓存的内部数据结构是同步的,以保护您免受这种污染。你头脑中的模型应该是:只要一个线程正在使用缓存,它就有自己的副本。
因此第一个线程触发缓存加载。番石榴“克隆”缓存(在内部,它只是确保没有人可以改变线程1看到的结构)。 4秒后,线程获取缓存加载返回的结果,无论有多少其他线程在平均时间内更改了值(它们都有自己的“副本”进行修改)。
当线程1完成时,缓存会自行更新。现在,线程2的变化对于线程1变得可见。