2017-02-21 108 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

奇怪「來自(供應商)」強制使用「對象」鍵,所以很遺憾,它並不是非常有用。 –