2017-02-21 1 views
2

私はグアバキャッシュに、ストアのユーザーIDをミューテックスキャッシュに持っています。グアバキャッシュ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; 
} 

次に、mutexオブジェクトとsynchronizedセクションを使用しています。私は、異なるスレッドからの同じユーザーが、同じキーで別のタスクを完了するのを待つことを期待しています。

私は、スレッド1

synchronized (getMutex(1)) { 
} 

を持っている場合は、スレッド2が完成し、実行する前に同期残すために、スレッド1を待つことになるが、それはそれは起こらないことが判明し、スレッドがお互いを待っていないのは、言ってみましょう。

グアバキャッシュをasMap()メソッドを使用してマップすると、私はレースをしたことがありますか?

+2

代わりに、キャッシュの代わりにGuavaのロック用に「Striped」を使用することを検討してください。 –

+0

@BenManesは私には合いません。内部に十分なストライプ(ロック)が付いたStripedを作成しても、別のユーザーがお互いを待っているときに衝突する可能性があります。 – user12384512

+0

大きな怠惰な弱いストライプは弱い価値のあるマップです。そうすること、または同じことを直接行うことは、より安全な追放政策になります。 –

答えて

2

Stripedbetter for your use caseないかどうかを確認、@BenManes mentioned in commentとして)、あなたはここに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契約があるので、あなたは、任意の競合状態を持っています:

このキャッシュのkeyに関連付けられた値を返します。必要に応じて、最初にその値をロードします。このキャッシュに関連する観察可能な状態は、ロードが完了するまで変更されません。

さらに、方法getMutexはおそらく冗長です。

+1

fyiでは、 'CacheLoader.from(function)'を使ってローダーを1ライナーとして書くことができます。これはlambdaでうまく動作します。 –

+0

@BenManesありがとう、私が上記のコードを書いたとき、私はそのようなものがGuavaに存在すべきかどうか疑問に思った。 'CacheLoader.from(supplier)'もサポートしているので、 'Object :: new'もうまく収まります。 – Xaerxess

+0

奇妙なことに 'from(supplier)'は 'Object'キーを強制します。 –

関連する問題