2017-02-21 69 views
2

我有番石榴缓存存储userId互斥缓存。番石榴缓存asMap方法

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()方法转换番石榴缓存地图?

+2

请考虑使用Guava的“Striped”进行锁定,而不是缓存。 –

+0

@BenManes不适合我。即使我在里面创建了带有足够条纹(锁)的Striped,当不同的用户互相等待时,我可能会发生碰撞。 – user12384512

+0

大型懒惰弱条纹是一个弱值地图。所以,或者直接做同样的事情,将会是一个更安全的驱逐政策。 –

回答

2

撇开你的闭锁机构(如@BenManes mentioned in comment,看看Stripedbetter 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可能是多余的。

+1

fyi,您可以使用'CacheLoader.from(function)'将加载器编写为一行代码,该代码可与lambda表达式很好地配合使用。 –

+0

@BenManes谢谢,当我编写上面的代码时,我想知道番石榴是否应该存在这样的事情,它确实存在!它也支持'CacheLoader.from(supplier)',所以'Object :: new'也很合适。 – Xaerxess

+0

奇怪“来自(供应商)”强制使用“对象”键,所以很遗憾,它并不是非常有用。 –