Java 如何防止多次同时加载非缓存值?
如何防止以高效的方式多次加载缓存中不存在的值 典型的缓存使用是以下伪代码:Java 如何防止多次同时加载非缓存值?,java,performance,caching,Java,Performance,Caching,如何防止以高效的方式多次加载缓存中不存在的值 典型的缓存使用是以下伪代码: Object get(Object key) { Object value = cache.get(key); if (value == null) { value = loadFromService(key); cache.set(key,value); } return value; } 问题是:在从服务(数据库、WebService、RemoteEJB或其他任何东西)加载值之前,可能会同时进行第二
Object get(Object key) {
Object value = cache.get(key);
if (value == null) {
value = loadFromService(key);
cache.set(key,value);
}
return value;
}
问题是:在从服务(数据库、WebService、RemoteEJB或其他任何东西)加载值之前,可能会同时进行第二次调用,这将再次加载值
例如,当我为用户X缓存所有项目时,该用户经常被查看,并且有许多项目,很有可能同时调用其所有项目的负载,从而导致服务器上的负载过重
我可以使get
功能同步化,但这会迫使其他搜索等待,没有多大意义。我可以为每个密钥创建新锁,但我不知道在Java中管理如此大量的锁是否是个好主意(这部分是特定于语言的,我将其标记为Java
)
或者我可以使用另一种方法?如果是这样的话,最有效的方法是什么?通常可以使用对象的哈希代码 您可以有一个基于哈希代码使用的锁数组,以减少冲突的机会。或者作为一个黑客,你可以利用自动装箱字节总是返回相同的对象这一事实
Object get(Object key) {
Object value = cache.get(key);
if (value == null) {
// every possible Byte is cached by the JLS.
Byte b = Byte.valueOf((byte) key.hashCode());
synchronized (b) {
value = cache.get(key);
if (value == null) {
value = loadFromService(key);
cache.set(key, value);
}
}
}
return value;
}
不要重新发明轮子,用番石榴或其他蔬菜
如果您正在使用Ehcache,请阅读,这就是您所要求的模式。必须实现
CacheEntryFactory
接口,以指示缓存如何读取缓存未命中的对象,并且必须在加载时将Ehcache
实例包装为SelfPopulatingCache
,在映射中插入中间对象而不是结果,以指示加载已开始但尚未完成。以下java.util.concurrent.FutureTask用于中间对象:
Object get(final Object key) throws Exception {
boolean doRun = false;
Object value;
synchronized (cache) {
value = cache.get(key);
if (value == null) {
value = new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
Object loadedValue = loadFromService(key);
synchronized (cache) {cache.put(key, loadedValue);};
return loadedValue;
}
});
cache.put(key, value);
doRun=true;
}
}
if (value instanceof FutureTask) {
FutureTask task = (FutureTask) value;
if (doRun) {
task.run();
}
return task.get();
}
return value;
}`
你想得太多了,说真的。除非从服务加载数据的时间太长,否则这永远不会是问题。我有一些外来的EJB代码,在测试环境中可能需要20秒,所以我担心10或20个并发请求会发生什么基于哈希代码的锁池的好主意!但在获得锁后,您可以找到其他进程缓存的值,所以您还应该检查它是否已加载:)哇,我从未想过以这种方式使用字节!更重要的是,这是我第一次看到valueOf:)的字节值池的实际用法。你能解释一下“每个可能的字节都由JLS缓存”是什么意思吗?是像JVM从一开始就缓存它们,还是因为它们的不可变性,它们最终会被缓存在内存中?好吧,也许这就是答案:据我所知,CacheLoader正在做我所期望的事情,对所需的同步进行内部管理?是的,它提供了更多的功能-逐出、删除侦听器等等。我明白了,有趣的是,我使用的是EcHACHE,但我可以考虑使用番石榴,但是EHCACH支持溢出到磁盘,而且,如何实现它本身是有趣的。如果您正在使用EHCHACE,请阅读,这是您要的模式。我认为Ehcache也支持这一点。嗯,您的解决方案要求始终对整个缓存进行同步,但同步部分相当快。您认为如何在初始获取时执行,并仅在值为null时运行synchronized part?这取决于您期望每秒对缓存的请求数。同步部分的长度小于1微秒,因此,如果您的速率小于每秒100000个请求,则冲突的可能性可以忽略不计,因此任何复杂情况都不会产生任何影响。如果速率更高,那么还有另一种情况,您必须考虑许多不同的因素,包括处理器缓存、线程切换和垃圾收集器,从性能角度来看,对缓存的访问可能不是第一位的。