Java 如何确保多个番石榴缓存不会相互锁定?
据我所知,番石榴的缓存默认锁定在一把钥匙上。因此,如果线程t1和线程t2都试图获取相同的密钥,那么实际上只有一个线程会加载它,而另一个线程等待第一个线程获取值,然后获取相同的值 这是一个非常好的默认行为,但如果您处理的是相互依赖的多个缓存,那么它就不是最佳的 我们所处的情况是,我们有多个缓存实例和多个线程。线程查询多个缓存以完成其工作。因此,缓存实例相互依赖。它实际上可以归结为以下场景: 螺纹t1Java 如何确保多个番石榴缓存不会相互锁定?,java,caching,concurrency,locking,guava,Java,Caching,Concurrency,Locking,Guava,据我所知,番石榴的缓存默认锁定在一把钥匙上。因此,如果线程t1和线程t2都试图获取相同的密钥,那么实际上只有一个线程会加载它,而另一个线程等待第一个线程获取值,然后获取相同的值 这是一个非常好的默认行为,但如果您处理的是相互依赖的多个缓存,那么它就不是最佳的 我们所处的情况是,我们有多个缓存实例和多个线程。线程查询多个缓存以完成其工作。因此,缓存实例相互依赖。它实际上可以归结为以下场景: 螺纹t1 Value v1 = cache1.get(k, new Callable<Value>
Value v1 = cache1.get(k, new Callable<Value>() {
Value call() {
//do something
Value v2 = cache2.get(k, doRealWorkCallable());
Value v = calculateFrom(v2)
return v;
}
});
(当然,第二个线程的情况正好相反)
这带来了“可能不需要”计算值的成本,好处是不存在死锁线程的风险
有没有“更好”的方法来处理番石榴
编辑:下面是一个具体的例子
我们正在从无法控制的外部系统调用多个Web服务。这些Web服务提供的数据是分层的,并通过引用链接。例如:
class WSOrganization {
Integer id;
String name;
List<Integer> employeeIds; //like a collection of foreign keys
}
class WSEmployee {
Integer id;
String name;
Integer organizationId; //like a foreignkey
}
现在,我们想通过在EJB1和EJB2上使用javax.interceptor.interceptor
来介绍缓存
@AroundInvoke
public Object aroundInvoke(InvocationContext invocation) {
Object object = getElementFromCache(invocation);
return object;
}
两个线程可能会以相反的顺序调用这两个方法,我们肯定不希望它们相互阻塞
EDIT2:使用Hashmaps实现getElementFromCache()的示例
Guava在为缓存执行Callable时似乎没有锁定密钥。否则,您提供的代码将始终死锁,例如:
getOrganization(1337):
(contains employee X)
getEmployee(x):
getOrganization(1337) // deadlock by recursion!!!
各国:
这个方法提供了一个简单的替代传统的“如果缓存,则返回;否则创建,则缓存并返回”模式
请注意,我没有尝试过这一点,但仅从文档中可以看出,Guava似乎在多次运行您的Callable时出错
总之,这不是问题 你怎么可能有这样相互依赖的值呢?我不理解它在任何应用程序中的意义。我们调用外部应用程序的Web服务并检索分层数据。我将为这个问题添加一个具体的例子。忘记Guava缓存,您可能会如何正常执行?使用添加到上述问题的hashmaps实现getElementFromCache()(为什么不能将代码片段添加到注释中?)。
//in EJB 1
PrefetchedOrganization getOrganization(Integer orgId) {
WSOrganization org = orgService.getOrganizationById(orgId);
for (Integer employeeId : org.employeeIds) {
WSEmployee employee = employeeService.getEmployeeById(employeeId);
listOfEmployees.add(employee);
}
return createPrefetchedOrgWithEmployees(org, listOfEmployees);
}
//in EJB 2
PrefetchedEmployee getEmployee(Integer employeeId) {
WSEmployee employee = employeeService.getEmployeeById(employeeId);
PrefetchedOrganization orgOfEmployee = ejb2.getOrganization(employee.organisationId);
return orgOfEmployee.employee(employeeId);
}
@AroundInvoke
public Object aroundInvoke(InvocationContext invocation) {
Object object = getElementFromCache(invocation);
return object;
}
Integer id = idFrom(invocation);
if (cache.containsKey(id)) {
return cache.get(id);
} else {
Object result = invocation.proceed();
cache.put(id, result);
return result;
}
getOrganization(1337):
(contains employee X)
getEmployee(x):
getOrganization(1337) // deadlock by recursion!!!