2017-02-21 5 views
2

У меня есть кэш guava с хранилищем userId в кэш-память мьютекса.Guava cache 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. Я ожидаю, что тот же пользователь из разных потоков будет ждать, пока еще одна задача будет завершена одним и тем же ключом.

Скажем, если у меня есть нить 1

synchronized (getMutex(1)) { 
} 

Затем нить 2 будет ждать потока 1, чтобы оставить синхронизируется до того закончил исполнение, но оказывается, что это не произойдет, нити не ждут друг от друга ,

Возможно, у меня есть гонка при преобразовании кеша guava для сопоставления с использованием метода asMap()?

+2

Рассмотрите возможность использования «Полосатого» Guava для блокировки вместо кеша. –

+0

@BenManes не подходит для меня. Даже если я создаю Striped с достаточным количеством полос (замков) внутри, я могу столкнуться, когда разные пользователи ждут друг друга. – user12384512

+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 был бы, вероятно, излишним.

+1

fyi, вы можете написать загрузчик как однострочный, используя 'CacheLoader.from (function)', который отлично играет с lambdas. –

+0

@BenManes Спасибо, когда я написал код выше, я задавался вопросом, существует ли такая вещь в Гуаве, и это так! Он также поддерживает 'CacheLoader.from (поставщик)' поэтому 'Object :: new' тоже хорошо подходит. – Xaerxess

+0

Странно 'от (поставщика)' заставляет ключ 'Object', так что, к сожалению, это не очень полезно, как есть. –