[英]How to assure multiple guava caches do not lock each other?
據我了解,默認情況下,番石榴的緩存鎖定在一個鍵上。 因此,如果線程t1和線程t2都嘗試獲取相同的密鑰,則只有一個線程實際上會加載它,而另一個線程等待第一個線程獲取值,然后再獲取相同的密鑰。
如果您要處理相互依賴的多個緩存,這是一個很好的默認行為,但並不是最佳選擇。
我們處於一種情況,其中我們有多個緩存實例和多個線程。 線程查詢多個緩存以完成其工作。 因此,緩存實例相互依賴。 實際上,可以歸結為以下情況:
線程t1
Value v1 = cache1.get(k, new Callable<Value>() {
Value call() {
//do something
Value v2 = cache2.get(k, doRealWorkCallable());
Value v = calculateFrom(v2)
return v;
}
});
線程t2
Value v2 = cache2.get(k, new Callable<Value>() {
Value call() {
//do something
Value v1 = cache1.get(k, doRealWorkCallable());
Value v = calculateFrom(v1)
return v;
}
});
如果我正確理解了鎖定策略,則上述情況可能會導致死鎖:線程t1在cache1中持有k的鎖,在cache2中等待k的鎖。 線程t2為cache2中的k持有鎖,等待cache1中的k為鎖。
番石榴有什么辦法可以防止這種僵局? 如我所見,只要您使用CacheLoader
或Callable
,您就可能陷入僵局,因為兩者都鎖定了它們正在加載的密鑰。
我認為我們可以使用舊的“檢查是否存在於緩存中,如果不存在:計算並放置在緩存中”:
Value v1 = cache1.getIfPresent(k);
if (v1 == null) {
//get it using cache2
Value v2 = cache2.getIfPresent(k);
if (v2 == null) {
v2 = doRealWork();
cache2.put(k,v2);
}
v1 = calculateFrom(v2);
cache1.put(v1);
}
(當然,第二個線程也可以相反)
這帶來了“可能不需要”的值計算成本,而不會冒死鎖線程的風險。
番石榴有什么更好的方法嗎?
我們正在從無法控制的外部系統調用多個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
}
在某些地方我們需要員工,在某些地方我們需要組織。 如果我們要求組織,我們會很熱心。 如果我們拿到員工,我們也需要組織。 該代碼分布在多個服務存根之間,依此類推,但最終歸結為:
//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);
}
現在,我們想通過在EJB 1和EJB 2上使用javax.interceptor.Interceptor
引入緩存。
@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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.