我有guava缓存,其中存储userId为mutex缓存。
Cache<Long, Object> byUserIdMutex = CacheBuilder.newBuilder()
.concurrencyLevel(4)
.weakKeys()
.maximumSize(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
private Object getMutex(long userId) {
Object newLock = new Object();
Object old = byUserIdMutex.asMap().putIfAbsent(userId, newLock);
if (old != null) {
return old;
}
return newLock;
}
然后我使用带有互斥对象的synchronized部分。我希望不同线程的同一个用户可以通过相同的密钥等待另一个任务完成。
假设我有线程1
synchronized (getMutex(1)) {
}
然后线程2将等待线程1在完成执行之前保持同步,但事实证明它不会发生,线程不会等待彼此。
使用asMap()方法将番石榴缓存转换为地图时,也许我有竞争对手?
答案 0 :(得分:2)
不管你的锁定机制(@BenManes mentioned in comment,看Striped
是不是better for your use case),你应该在这里使用LoadingCache
:
LoadingCache<Long, Object> byUserIdMutex = CacheBuilder.newBuilder()
.concurrencyLevel(4)
.weakKeys()
.maximumSize(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(CacheLoader.from(Object::new));
private Object getMutex(long userId) {
return byUserIdMutex.getUnchecked(userId);
}
这样你就不会有任何竞争条件,因为getUnchecked
合同是:
返回与此缓存中的键关联的值,必要时首先加载该值。在加载完成之前,不会修改与此缓存关联的可观察状态。
另外,方法getMutex
可能是多余的。