|
Cache на основе ConcurrentHashMap. Метод computeIfAbsent блокируется.
|
|||
---|---|---|---|
#18+
Коллеги, такое дело. В спринге есть аспект @Cacheable, который кеширует результат высичления метода. Есть еще JSR107 с аннотацией @CacheResult. Суть одна и та же. Для своего проекта я сделал нечто похожее, аспекты на основе CDI (мне для своих нужд было удобнее свое написать), выглядит так: Код: java 1. 2. 3. 4.
В примере вверху используется кеш SessionScoped, т.е. кеш будет жить только пока http сессия клиента жива и проинвалидируется, когда сессия умрет. Помимо этого есть еще @RequestCache и @ApplicationCache. Второе условие, когда кеш может быть сброшен, можно указать в тегах. В примере наверху, это tags = Tag.DOCUMENT_TYPES. Т.е. если аспект обнаруживает, что DOCUMENT_TYPES поменялись, то сбрасывает кеш. Такая работа меня полностью устраивает. Внутри использую ConcurrentHashMap. Так как значения в кеше еще может не быть, и несколько потоков одновременно вызовят этот метод, я не хочу, чтобы метод выполнился несколько раз, поэтому я использую метод ConcurrentHaspMap#computeIfAbsent, который гарантирует нам, что функция вычисляющая значение, будет выполнена один раз. Это аналог того, что дает спринг в аннотации @Cacheable(sync=true) Так вот оказалось, что метод ConcurrentHaspMap#computeIfAbsent может блокирнуться при таком сценарии: Код: java 1.
Т.е. когда мы рекурсивно вызываем computeIfAbsent внутри функции вычисления значения. Про это, в принципе, написано в документации к ConcurrentHashMap: Some attempted update operations on this map by other threads may be blocked while computation is in progress, so the computation should be short and simple, and must not attempt to update any other mappings of this map. Такая ситуация возможна например если мы имеем два метода оба аннотированные @SessionCache, и если один вызывает второй: Код: java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.
Я начал разбираться, что там в EHCache, ведь для on heap кеша он использует тот же ConcurrentHashMap И у него такой проблемы не возникло! И я обнаружил в исходниках, что они используют не computeIfAbsent, а просто compute. И именно этот метод не блокируется при рекурсивном вызове (по крайней мере на моих данных). Но к этому методу тоже написано в документации Some attempted update operations on this map by other threads may be blocked while computation is in progress, so the computation should be short and simple, and must not attempt to update any other mappings of this map. Как-то стремно получить такую блокировку на боевом было бы. Так что я наверное запрещу вызов метода с аспектом кеширования, если в цепочке вызовов уже такой имелся. А вы что думаете? ... |
|||
:
Нравится:
Не нравится:
|
|||
02.12.2019, 19:32 |
|
|
start [/forum/topic.php?fid=59&fpage=20&tid=2121005]: |
0ms |
get settings: |
10ms |
get forum list: |
14ms |
check forum access: |
4ms |
check topic access: |
4ms |
track hit: |
36ms |
get topic data: |
10ms |
get forum data: |
3ms |
get page messages: |
40ms |
get tp. blocked users: |
2ms |
others: | 17ms |
total: | 140ms |
0 / 0 |