From 103e16eee42c84b37ce3013bbbd2ff2c1b8dfbab Mon Sep 17 00:00:00 2001 From: Nikita Koksharov Date: Tue, 12 Dec 2023 13:32:36 +0300 Subject: [PATCH] Improvement - Virtual Threads compatibility #5499 --- .../org/redisson/cache/AbstractCacheMap.java | 30 +++++++------ .../java/org/redisson/cache/CachedValue.java | 4 ++ .../java/org/redisson/cache/LFUCacheMap.java | 45 ++++++++++--------- .../java/org/redisson/cache/LRUCacheMap.java | 19 +++++--- .../org/redisson/cache/StdCachedValue.java | 8 ++++ 5 files changed, 65 insertions(+), 41 deletions(-) diff --git a/redisson/src/main/java/org/redisson/cache/AbstractCacheMap.java b/redisson/src/main/java/org/redisson/cache/AbstractCacheMap.java index 0398cdf82..3f6278d4f 100644 --- a/redisson/src/main/java/org/redisson/cache/AbstractCacheMap.java +++ b/redisson/src/main/java/org/redisson/cache/AbstractCacheMap.java @@ -15,6 +15,8 @@ */ package org.redisson.cache; +import org.redisson.misc.WrappedLock; + import java.util.*; import java.util.AbstractMap.SimpleEntry; import java.util.concurrent.ConcurrentHashMap; @@ -486,16 +488,16 @@ public abstract class AbstractCacheMap implements Cache { @Override public boolean remove(Object key, Object value) { - CachedValue e = null; - synchronized (map) { + CachedValue e = lock.execute(() -> { CachedValue entry = map.get(key); if (entry != null && entry.getValue().equals(value) && !isValueExpired(entry)) { map.remove(key); - e = entry; + return entry; } - } + return null; + }); if (e != null) { onValueRemove(e); return true; @@ -503,19 +505,21 @@ public abstract class AbstractCacheMap implements Cache { return false; } + private final WrappedLock lock = new WrappedLock(); + @Override public boolean replace(K key, V oldValue, V newValue) { - CachedValue e = null; - synchronized (map) { + CachedValue e = lock.execute(() -> { CachedValue entry = map.get(key); if (entry != null && entry.getValue().equals(oldValue) - && !isValueExpired(entry)) { + && !isValueExpired(entry)) { CachedValue newEntry = create(key, newValue, timeToLiveInMillis, maxIdleInMillis); map.put(key, newEntry); - e = entry; + return entry; } - } + return null; + }); if (e != null) { onValueRemove(e); return true; @@ -525,16 +529,16 @@ public abstract class AbstractCacheMap implements Cache { @Override public V replace(K key, V value) { - CachedValue e = null; - synchronized (map) { + CachedValue e = lock.execute(() -> { CachedValue entry = map.get(key); if (entry != null && !isValueExpired(entry)) { CachedValue newEntry = create(key, value, timeToLiveInMillis, maxIdleInMillis); map.put(key, newEntry); - e = entry; + return entry; } - } + return null; + }); if (e != null) { onValueRemove(e); return e.getValue(); diff --git a/redisson/src/main/java/org/redisson/cache/CachedValue.java b/redisson/src/main/java/org/redisson/cache/CachedValue.java index a9f3c5b62..c67835ae9 100644 --- a/redisson/src/main/java/org/redisson/cache/CachedValue.java +++ b/redisson/src/main/java/org/redisson/cache/CachedValue.java @@ -15,6 +15,8 @@ */ package org.redisson.cache; +import org.redisson.misc.WrappedLock; + /** * Created by jribble on 2/20/17. */ @@ -23,4 +25,6 @@ public interface CachedValue extends ExpirableValue { K getKey(); V getValue(); + + WrappedLock getLock(); } diff --git a/redisson/src/main/java/org/redisson/cache/LFUCacheMap.java b/redisson/src/main/java/org/redisson/cache/LFUCacheMap.java index 1a63f2b03..48b951074 100644 --- a/redisson/src/main/java/org/redisson/cache/LFUCacheMap.java +++ b/redisson/src/main/java/org/redisson/cache/LFUCacheMap.java @@ -57,12 +57,12 @@ public class LFUCacheMap extends AbstractCacheMap { } - public static class LFUCachedValue extends StdCachedValue { + public static class LFUCachedValue extends StdCachedValue { private final Long id; private long accessCount; - public LFUCachedValue(long id, Object key, Object value, long ttl, long maxIdleTime) { + public LFUCachedValue(long id, K key, V value, long ttl, long maxIdleTime) { super(key, value, ttl, maxIdleTime); this.id = id; } @@ -74,46 +74,47 @@ public class LFUCacheMap extends AbstractCacheMap { } private final AtomicLong idGenerator = new AtomicLong(); - private final ConcurrentNavigableMap accessMap = new ConcurrentSkipListMap(); + private final ConcurrentNavigableMap> accessMap = new ConcurrentSkipListMap<>(); public LFUCacheMap(int size, long timeToLiveInMillis, long maxIdleInMillis) { super(size, timeToLiveInMillis, maxIdleInMillis); } @Override - protected CachedValue create(K key, V value, long ttl, long maxIdleTime) { - return new LFUCachedValue(idGenerator.incrementAndGet(), key, value, ttl, maxIdleTime); + protected CachedValue create(K key, V value, long ttl, long maxIdleTime) { + return new LFUCachedValue(idGenerator.incrementAndGet(), key, value, ttl, maxIdleTime); } @Override - protected void onValueCreate(CachedValue value) { - MapKey key = toKey((LFUCachedValue) value); - accessMap.put(key, (LFUCachedValue) value); + protected void onValueCreate(CachedValue value) { + MapKey key = toKey((LFUCachedValue) value); + accessMap.put(key, (LFUCachedValue) value); } @Override - protected void onValueRead(CachedValue value) { - addAccessCount((LFUCachedValue) value, 1); + protected void onValueRead(CachedValue value) { + addAccessCount((LFUCachedValue) value, 1); } - private MapKey toKey(LFUCachedValue value) { + private MapKey toKey(LFUCachedValue value) { return new MapKey(value.accessCount, value); } @Override - protected void onValueRemove(CachedValue value) { - synchronized (value) { - MapKey key = toKey((LFUCachedValue) value); + protected void onValueRemove(CachedValue value) { + value.getLock().execute(() -> { + MapKey key = toKey((LFUCachedValue) value); accessMap.remove(key); - } + }); } - private void addAccessCount(LFUCachedValue value, long count) { - synchronized (value) { + private void addAccessCount(LFUCachedValue value, long c) { + value.getLock().execute(() -> { + long count = c; if (count < 0 && value.accessCount == 0) { return; } - + MapKey key = toKey(value); if (accessMap.remove(key) == null) { return; @@ -123,15 +124,15 @@ public class LFUCacheMap extends AbstractCacheMap { count = -Math.min(value.accessCount, -count); } value.addAccessCount(count); - + key = toKey(value); accessMap.put(key, value); - } + }); } @Override protected void onMapFull() { - Map.Entry entry = accessMap.pollFirstEntry(); + Map.Entry> entry = accessMap.pollFirstEntry(); if (entry == null) { return; } @@ -143,7 +144,7 @@ public class LFUCacheMap extends AbstractCacheMap { // TODO optimize // decrease all values - for (LFUCachedValue value : accessMap.values()) { + for (LFUCachedValue value : accessMap.values()) { addAccessCount(value, -entry.getValue().accessCount); } } diff --git a/redisson/src/main/java/org/redisson/cache/LRUCacheMap.java b/redisson/src/main/java/org/redisson/cache/LRUCacheMap.java index 0b506f01f..cebd8366f 100644 --- a/redisson/src/main/java/org/redisson/cache/LRUCacheMap.java +++ b/redisson/src/main/java/org/redisson/cache/LRUCacheMap.java @@ -17,6 +17,8 @@ package org.redisson.cache; import java.util.*; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; /** * LRU (least recently used) cache. @@ -29,15 +31,16 @@ import java.util.concurrent.atomic.AtomicLong; public class LRUCacheMap extends AbstractCacheMap { private final AtomicLong index = new AtomicLong(); - private final List>> queues = - new ArrayList>>(); - + private final List>> queues = new ArrayList<>(); + private final Map>, Lock> queueLocks = new IdentityHashMap<>(); + public LRUCacheMap(int size, long timeToLiveInMillis, long maxIdleInMillis) { super(size, timeToLiveInMillis, maxIdleInMillis); for (int i = 0; i < Runtime.getRuntime().availableProcessors()*2; i++) { - Set> instance = Collections.synchronizedSet(new LinkedHashSet>()); + Set> instance = Collections.synchronizedSet(new LinkedHashSet<>()); queues.add(instance); + queueLocks.put(instance, new ReentrantLock()); } } @@ -60,7 +63,7 @@ public class LRUCacheMap extends AbstractCacheMap { @Override protected void onValueRead(CachedValue value) { Collection> queue = getQueue(value); - // move value to tail of queue + // move value to the tail of the queue if (queue.remove(value)) { queue.add(value); } @@ -80,12 +83,16 @@ public class LRUCacheMap extends AbstractCacheMap { Collection> queue = queues.get(queueIndex); CachedValue removedValue = null; - synchronized (queue) { + Lock lock = queueLocks.get(queue); + lock.lock(); + try { Iterator> iter = queue.iterator(); if (iter.hasNext()) { removedValue = iter.next(); iter.remove(); } + } finally { + lock.unlock(); } if (removedValue != null) { diff --git a/redisson/src/main/java/org/redisson/cache/StdCachedValue.java b/redisson/src/main/java/org/redisson/cache/StdCachedValue.java index 9b09ec040..5b83703cc 100644 --- a/redisson/src/main/java/org/redisson/cache/StdCachedValue.java +++ b/redisson/src/main/java/org/redisson/cache/StdCachedValue.java @@ -15,6 +15,8 @@ */ package org.redisson.cache; +import org.redisson.misc.WrappedLock; + /** * Created by jribble on 2/20/17. */ @@ -30,6 +32,8 @@ public class StdCachedValue implements CachedValue { private long creationTime; private long lastAccess; + private final WrappedLock lock = new WrappedLock(); + public StdCachedValue(K key, V value, long ttl, long maxIdleTime) { this.value = value; this.ttl = ttl; @@ -73,4 +77,8 @@ public class StdCachedValue implements CachedValue { return "CachedValue [key=" + key + ", value=" + value + "]"; } + @Override + public WrappedLock getLock() { + return lock; + } }