diff --git a/redisson/src/main/java/org/redisson/cache/AbstractCacheMap.java b/redisson/src/main/java/org/redisson/cache/AbstractCacheMap.java index 4383aa13c..1741c6d25 100644 --- a/redisson/src/main/java/org/redisson/cache/AbstractCacheMap.java +++ b/redisson/src/main/java/org/redisson/cache/AbstractCacheMap.java @@ -38,7 +38,7 @@ import io.netty.util.internal.PlatformDependent; public abstract class AbstractCacheMap implements Cache { final int size; - final ConcurrentMap map = PlatformDependent.newConcurrentHashMap(); + final ConcurrentMap> map = PlatformDependent.newConcurrentHashMap(); private final long timeToLiveInMillis; private final long maxIdleInMillis; @@ -52,11 +52,11 @@ public abstract class AbstractCacheMap implements Cache { this.timeToLiveInMillis = timeToLiveInMillis; } - protected void onValueRead(CachedValue value) { + protected void onValueRead(CachedValue value) { } - protected void onValueRemove(CachedValue value) { + protected void onValueRemove(CachedValue value) { } @@ -89,7 +89,7 @@ public abstract class AbstractCacheMap implements Cache { throw new NullPointerException(); } - CachedValue entry = map.get(key); + CachedValue entry = map.get(key); if (entry == null) { return false; } @@ -113,8 +113,8 @@ public abstract class AbstractCacheMap implements Cache { throw new NullPointerException(); } - for (Map.Entry entry : map.entrySet()) { - CachedValue cachedValue = entry.getValue(); + for (Map.Entry> entry : map.entrySet()) { + CachedValue cachedValue = entry.getValue(); if (cachedValue.getValue().equals(value)) { if (cachedValue.isExpired()) { if (map.remove(cachedValue.getKey(), cachedValue)) { @@ -139,7 +139,7 @@ public abstract class AbstractCacheMap implements Cache { throw new NullPointerException(); } - CachedValue entry = map.get(key); + CachedValue entry = map.get(key); if (entry == null) { return null; } @@ -153,8 +153,7 @@ public abstract class AbstractCacheMap implements Cache { return readValue(entry); } - @SuppressWarnings("unchecked") - protected V readValue(CachedValue entry) { + protected V readValue(CachedValue entry) { onValueRead(entry); return (V) entry.getValue(); } @@ -168,17 +167,16 @@ public abstract class AbstractCacheMap implements Cache { return put(key, value, timeToLiveInMillis, TimeUnit.MILLISECONDS, maxIdleInMillis, TimeUnit.MILLISECONDS); } - @SuppressWarnings("unchecked") @Override public V put(K key, V value, long ttl, TimeUnit ttlUnit, long maxIdleTime, TimeUnit maxIdleUnit) { - CachedValue entry = create(key, value, ttlUnit.toMillis(ttl), maxIdleUnit.toMillis(maxIdleTime)); + CachedValue entry = create(key, value, ttlUnit.toMillis(ttl), maxIdleUnit.toMillis(maxIdleTime)); if (isFull(key)) { if (!removeExpiredEntries()) { onMapFull(); } } onValueCreate(entry); - CachedValue prevCachedValue = map.put(key, entry); + CachedValue prevCachedValue = map.put(key, entry); if (prevCachedValue != null) { onValueRemove(prevCachedValue); if (!prevCachedValue.isExpired()) { @@ -188,17 +186,17 @@ public abstract class AbstractCacheMap implements Cache { return null; } - protected CachedValue create(K key, V value, long ttl, long maxIdleTime) { - return new StdCachedValue(key, value, ttl, maxIdleTime); + protected CachedValue create(K key, V value, long ttl, long maxIdleTime) { + return new StdCachedValue(key, value, ttl, maxIdleTime); } - protected void onValueCreate(CachedValue entry) { + protected void onValueCreate(CachedValue entry) { } - private boolean removeExpiredEntries() { + protected boolean removeExpiredEntries() { boolean removed = false; // TODO optimize - for (CachedValue value : map.values()) { + for (CachedValue value : map.values()) { if (value.isExpired()) { if (map.remove(value.getKey(), value)) { onValueRemove(value); @@ -232,10 +230,9 @@ public abstract class AbstractCacheMap implements Cache { * (non-Javadoc) * @see java.util.Map#remove(java.lang.Object) */ - @SuppressWarnings("unchecked") @Override public V remove(Object key) { - CachedValue entry = map.remove(key); + CachedValue entry = map.remove(key); if (entry != null) { onValueRemove(entry); if (!entry.isExpired()) { @@ -298,9 +295,9 @@ public abstract class AbstractCacheMap implements Cache { abstract class MapIterator implements Iterator { - final Iterator> keyIterator = map.entrySet().iterator(); + final Iterator>> keyIterator = map.entrySet().iterator(); - Map.Entry mapEntry; + Map.Entry> mapEntry; @Override public boolean hasNext() { @@ -309,7 +306,7 @@ public abstract class AbstractCacheMap implements Cache { } mapEntry = null; while (keyIterator.hasNext()) { - Map.Entry entry = keyIterator.next(); + Map.Entry> entry = keyIterator.next(); if (entry.getValue().isExpired()) { continue; } diff --git a/redisson/src/main/java/org/redisson/cache/CachedValueReference.java b/redisson/src/main/java/org/redisson/cache/CachedValueReference.java new file mode 100644 index 000000000..c6e0c4bfb --- /dev/null +++ b/redisson/src/main/java/org/redisson/cache/CachedValueReference.java @@ -0,0 +1,34 @@ +/** + * Copyright 2016 Nikita Koksharov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.redisson.cache; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; + +public class CachedValueReference extends SoftReference { + + private final CachedValue owner; + + public CachedValueReference(CachedValue owner, V referent, ReferenceQueue q) { + super(referent, q); + this.owner = owner; + } + + public CachedValue getOwner() { + return owner; + } + +} diff --git a/redisson/src/main/java/org/redisson/cache/SoftCacheMap.java b/redisson/src/main/java/org/redisson/cache/SoftCacheMap.java index 066b0a7de..d861fb37c 100644 --- a/redisson/src/main/java/org/redisson/cache/SoftCacheMap.java +++ b/redisson/src/main/java/org/redisson/cache/SoftCacheMap.java @@ -15,6 +15,8 @@ */ package org.redisson.cache; +import java.lang.ref.ReferenceQueue; + /** * * @author Nikita Koksharov @@ -24,14 +26,28 @@ package org.redisson.cache; */ public class SoftCacheMap extends AbstractCacheMap { + private final ReferenceQueue queue = new ReferenceQueue(); + public SoftCacheMap(long timeToLiveInMillis, long maxIdleInMillis) { super(0, timeToLiveInMillis, maxIdleInMillis); } - protected CachedValue create(K key, V value, long ttl, long maxIdleTime) { - return new SoftCachedValue(key, value, ttl, maxIdleTime); + protected CachedValue create(K key, V value, long ttl, long maxIdleTime) { + return new SoftCachedValue(key, value, ttl, maxIdleTime, queue); } + @Override + protected boolean removeExpiredEntries() { + while (true) { + CachedValueReference value = (CachedValueReference) queue.poll(); + if (value == null) { + break; + } + map.remove(value.getOwner().getKey(), value.getOwner()); + } + return super.removeExpiredEntries(); + } + @Override protected void onMapFull() { } diff --git a/redisson/src/main/java/org/redisson/cache/SoftCachedValue.java b/redisson/src/main/java/org/redisson/cache/SoftCachedValue.java index 05dae153b..efdedc100 100644 --- a/redisson/src/main/java/org/redisson/cache/SoftCachedValue.java +++ b/redisson/src/main/java/org/redisson/cache/SoftCachedValue.java @@ -15,36 +15,25 @@ */ package org.redisson.cache; -import java.lang.ref.SoftReference; +import java.lang.ref.ReferenceQueue; /** * Created by jribble on 2/20/17. */ -public class SoftCachedValue implements CachedValue { - StdCachedValue> value; +public class SoftCachedValue extends StdCachedValue implements CachedValue { + + private final CachedValueReference ref; - public SoftCachedValue(K key, V value, long ttl, long maxIdleTime) { - this.value = new StdCachedValue>(key, new SoftReference(value), ttl, maxIdleTime); - } - - @Override - public boolean isExpired() { - return value.isExpired(); - } - - @Override - public K getKey() { - return value.getKey(); + public SoftCachedValue(K key, V value, long ttl, long maxIdleTime, ReferenceQueue queue) { + super(key, null, ttl, maxIdleTime); + this.ref = new CachedValueReference(this, value, queue); } @Override public V getValue() { - return value.getValue().get(); + super.getValue(); + return ref.get(); } - @Override - public String toString() { - return value.toString(); - } } diff --git a/redisson/src/test/java/org/redisson/misc/SoftCacheMapTest.java b/redisson/src/test/java/org/redisson/misc/SoftCacheMapTest.java index ec8a02dc3..dd074932e 100644 --- a/redisson/src/test/java/org/redisson/misc/SoftCacheMapTest.java +++ b/redisson/src/test/java/org/redisson/misc/SoftCacheMapTest.java @@ -62,6 +62,7 @@ public class SoftCacheMapTest { assertThat(map.values().stream().filter(Objects::nonNull).count()).isEqualTo(100000); System.gc(); assertThat(map.values().stream().filter(Objects::nonNull).count()).isZero(); + assertThat(map.values().size()).isZero(); } }