Guava缓存asMap方法

Guava cache asMap method

我有 guava 缓存,其中将 userId 存储到互斥缓存。

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;
}

然后我将同步部分与互斥对象一起使用。我希望来自不同线程的同一个用户将通过相同的键等待另一个任务完成。

假设我有线程 1

synchronized (getMutex(1)) {
}

然后线程 2 将等待线程 1 在完成执行之前离开同步,但事实证明它并没有发生,线程没有相互等待。

也许我在使用 asMap() 方法将 guava 缓存转换为 map 时遇到了竞争?

抛开你的锁定机制(如, see if Striped isn't better for your use case), you should use 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 合同是:

Returns the value associated with key in this cache, first loading that value if necessary. No observable state associated with this cache is modified until loading completes.

此外,方法 getMutex 可能是多余的。