Maps replaced

pull/243/head
Nikita 10 years ago
parent af705534db
commit 70512124d4

@ -17,14 +17,12 @@ package org.redisson;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import org.redisson.client.RedisPubSubListener;
import org.redisson.client.protocol.LongCodec;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.client.protocol.StringCodec;
import org.redisson.client.protocol.pubsub.PubSubStatusMessage;
import org.redisson.connection.ConnectionManager;
import org.redisson.core.RCountDownLatch;
@ -32,6 +30,7 @@ import org.redisson.core.RCountDownLatch;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.PlatformDependent;
/**
* Distributed alternative to the {@link java.util.concurrent.CountDownLatch}
@ -47,7 +46,7 @@ public class RedissonCountDownLatch extends RedissonObject implements RCountDown
private static final Integer zeroCountMessage = 0;
private static final Integer newCountMessage = 1;
private static final ConcurrentMap<String, RedissonCountDownLatchEntry> ENTRIES = new ConcurrentHashMap<String, RedissonCountDownLatchEntry>();
private static final ConcurrentMap<String, RedissonCountDownLatchEntry> ENTRIES = PlatformDependent.newConcurrentHashMap();
private final UUID id;

@ -33,6 +33,7 @@ import io.netty.util.TimerTask;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.PlatformDependent;
/**
* Distributed implementation of {@link java.util.concurrent.locks.Lock}
@ -45,14 +46,14 @@ import io.netty.util.concurrent.Promise;
public class RedissonLock extends RedissonExpirable implements RLock {
public static final long LOCK_EXPIRATION_INTERVAL_SECONDS = 30;
private static final ConcurrentMap<String, Timeout> refreshTaskMap = new ConcurrentHashMap<String, Timeout>();
private static final ConcurrentMap<String, Timeout> refreshTaskMap = PlatformDependent.newConcurrentHashMap();
protected long internalLockLeaseTime = TimeUnit.SECONDS.toMillis(LOCK_EXPIRATION_INTERVAL_SECONDS);
private final UUID id;
private static final Integer unlockMessage = 0;
private static final ConcurrentMap<String, RedissonLockEntry> ENTRIES = new ConcurrentHashMap<String, RedissonLockEntry>();
private static final ConcurrentMap<String, RedissonLockEntry> ENTRIES = PlatformDependent.newConcurrentHashMap();
protected RedissonLock(ConnectionManager connectionManager, String name, UUID id) {
super(connectionManager, name);

@ -1,716 +0,0 @@
/**
* Copyright 2014 Nikita Koksharov, Nickolay Borbit
*
* 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.misc;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.lang.ref.ReferenceQueue;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Concurrent hash map that wraps keys and/or values in SOFT or WEAK references.
* Does not support <code>null</code> keys or values. Uses identity equality for
* weak and soft keys. Supports remove listener
*
* @author crazybob@google.com (Bob Lee)
* @author fry@google.com (Charles Fry)
* @author igor.spasic@gmail.com
* @author Nikita Koksharov
*/
@SuppressWarnings("unchecked")
public class ReferenceMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> {
public interface RemoveValueListener<V> {
void onRemove(V value);
}
public enum ReferenceType {
/**
* Prevents referent from being reclaimed by the garbage collector.
*/
STRONG,
/**
* Referent reclaimed in an LRU fashion when the VM runs low on memory
* and no strong references exist.
*
* @see java.lang.ref.SoftReference
*/
SOFT,
/**
* Referent reclaimed when no strong or soft references exist.
*
* @see java.lang.ref.WeakReference
*/
WEAK,
/**
* Similar to weak references except the garbage collector doesn't
* actually reclaim the referent. More flexible alternative to
* finalization.
*
* @see java.lang.ref.PhantomReference
*/
PHANTOM,
}
protected ConcurrentMap<Object, Object> delegate;
protected final ReferenceType keyReferenceType;
protected final ReferenceType valueReferenceType;
public ReferenceMap(ReferenceType keyReferenceType, ReferenceType valueReferenceType) {
this(keyReferenceType, valueReferenceType, null);
}
/**
* Concurrent hash map that wraps keys and/or values based on specified
* reference types.
*
* @param keyReferenceType
* key reference type
* @param valueReferenceType
* value reference type
*/
public ReferenceMap(ReferenceType keyReferenceType, ReferenceType valueReferenceType, final RemoveValueListener<V> removeValueListener) {
if ((keyReferenceType == null) || (valueReferenceType == null)) {
throw new IllegalArgumentException("References types can not be null");
}
if (keyReferenceType == ReferenceType.PHANTOM || valueReferenceType == ReferenceType.PHANTOM) {
throw new IllegalArgumentException("Phantom references not supported");
}
this.delegate = new ConcurrentHashMap<Object, Object>() {
@Override
public Object remove(Object key) {
Object res = super.remove(key);
if (res != null && removeValueListener != null) {
removeValueListener.onRemove((V)res);
}
return res;
}
@Override
public boolean remove(Object key, Object value) {
boolean res = super.remove(key, value);
if (res && removeValueListener != null) {
removeValueListener.onRemove((V)value);
}
return res;
}
};
this.keyReferenceType = keyReferenceType;
this.valueReferenceType = valueReferenceType;
}
// ---------------------------------------------------------------- map
// implementations
@Override
public V get(final Object key) {
Object valueReference = delegate.get(makeKeyReferenceAware(key));
return dereferenceValue(valueReference);
}
private V execute(Strategy strategy, K key, V value) {
Object keyReference = referenceKey(key);
return (V) strategy.execute(this, keyReference, referenceValue(keyReference, value));
}
@Override
public V put(K key, V value) {
return execute(PutStrategy.PUT, key, value);
}
@Override
public V remove(Object key) {
Object referenceAwareKey = makeKeyReferenceAware(key);
Object valueReference = delegate.remove(referenceAwareKey);
return dereferenceValue(valueReference);
}
@Override
public int size() {
return delegate.size();
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public boolean containsKey(Object key) {
Object referenceAwareKey = makeKeyReferenceAware(key);
return delegate.containsKey(referenceAwareKey);
}
@Override
public boolean containsValue(Object value) {
for (Object valueReference : delegate.values()) {
if (value.equals(dereferenceValue(valueReference))) {
return true;
}
}
return false;
}
@Override
public void putAll(Map<? extends K, ? extends V> t) {
for (Map.Entry<? extends K, ? extends V> entry : t.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
@Override
public void clear() {
delegate.clear();
}
public V putIfAbsent(K key, V value) {
return execute(PutStrategy.PUT_IF_ABSENT, key, value);
}
public boolean remove(Object key, Object value) {
return delegate.remove(makeKeyReferenceAware(key), makeValueReferenceAware(value));
}
public boolean replace(K key, V oldValue, V newValue) {
Object keyReference = referenceKey(key);
Object referenceAwareOldValue = makeValueReferenceAware(oldValue);
return delegate.replace(keyReference, referenceAwareOldValue, referenceValue(keyReference, newValue));
}
public V replace(K key, V value) {
return execute(PutStrategy.REPLACE, key, value);
}
// ----------------------------------------------------------------
// conversions
/**
* Dereferences an entry. Returns <code>null</code> if the key or value has
* been gc'ed.
*/
Entry dereferenceEntry(Map.Entry<Object, Object> entry) {
K key = dereferenceKey(entry.getKey());
V value = dereferenceValue(entry.getValue());
return (key == null || value == null) ? null : new Entry(key, value);
}
/**
* Creates a reference for a key.
*/
Object referenceKey(K key) {
switch (keyReferenceType) {
case STRONG:
return key;
case SOFT:
return new SoftKeyReference(key);
case WEAK:
return new WeakKeyReference(key);
default:
throw new AssertionError();
}
}
/**
* Converts a reference to a key.
*/
K dereferenceKey(Object o) {
return (K) dereference(keyReferenceType, o);
}
/**
* Converts a reference to a value.
*/
V dereferenceValue(Object o) {
if (o == null) {
return null;
}
Object value = dereference(valueReferenceType, o);
if (o instanceof InternalReference) {
InternalReference reference = (InternalReference) o;
if (value == null) {
reference.finalizeReferent(); // old value was garbage collected
}
}
return (V) value;
}
/**
* Returns the refererent for reference given its reference type.
*/
private Object dereference(ReferenceType referenceType, Object reference) {
return referenceType == ReferenceType.STRONG ? reference : ((Reference) reference).get();
}
/**
* Creates a reference for a value.
*/
Object referenceValue(Object keyReference, Object value) {
switch (valueReferenceType) {
case STRONG:
return value;
case SOFT:
return new SoftValueReference(keyReference, value);
case WEAK:
return new WeakValueReference(keyReference, value);
default:
throw new AssertionError();
}
}
/**
* Wraps key so it can be compared to a referenced key for equality.
*/
private Object makeKeyReferenceAware(Object o) {
return keyReferenceType == ReferenceType.STRONG ? o : new KeyReferenceAwareWrapper(o);
}
/**
* Wraps value so it can be compared to a referenced value for equality.
*/
private Object makeValueReferenceAware(Object o) {
return valueReferenceType == ReferenceType.STRONG ? o : new ReferenceAwareWrapper(o);
}
// ---------------------------------------------------------------- inner
// classes
/**
* Marker interface to differentiate external and internal references. Also
* duplicates FinalizableReference and Reference.get for internal use.
*/
interface InternalReference {
/**
* Invoked on a background thread after the referent has been garbage
* collected.
*/
void finalizeReferent();
Object get();
}
/**
* Tests weak and soft references for identity equality. Compares references
* to other references and wrappers. If o is a reference, this returns true
* if r == o or if r and o reference the same non-null object. If o is a
* wrapper, this returns true if r's referent is identical to the wrapped
* object.
*/
private static boolean referenceEquals(Reference r, Object o) {
if (o instanceof InternalReference) { // compare reference to reference.
if (o == r) { // are they the same reference? used in cleanup.
return true;
}
Object referent = ((Reference) o).get(); // do they reference
// identical values? used
// in conditional puts.
return referent != null && referent == r.get();
}
return ((ReferenceAwareWrapper) o).unwrap() == r.get(); // is the
// wrapped
// object
// identical to
// the referent?
// used in
// lookups.
}
/**
* Returns <code>true</code> if the specified value reference has been
* garbage collected. The value behind the reference is also passed in,
* rather than queried inside this method, to ensure that the return
* statement of this method will still hold true after it has returned (that
* is, a value reference exists outside of this method which will prevent
* that value from being garbage collected).
*
* @param valueReference
* the value reference to be tested
* @param value
* the object referenced by <code>valueReference</code>
* @return <code>true</code> if <code>valueReference</code> is non-null and
* <code>value</code> is <code>null</code>
*/
private static boolean isExpired(Object valueReference, Object value) {
return (valueReference != null) && (value == null);
}
/**
* Big hack. Used to compare keys and values to referenced keys and values
* without creating more references.
*/
static class ReferenceAwareWrapper {
final Object wrapped;
ReferenceAwareWrapper(Object wrapped) {
this.wrapped = wrapped;
}
Object unwrap() {
return wrapped;
}
@Override
public int hashCode() {
return wrapped.hashCode();
}
@Override
public boolean equals(Object obj) {
return obj.equals(this); // defer to references equals() logic.
}
}
/**
* Used for keys. Overrides hash code to use identity hash code.
*/
static class KeyReferenceAwareWrapper extends ReferenceAwareWrapper {
KeyReferenceAwareWrapper(Object wrapped) {
super(wrapped);
}
@Override
public int hashCode() {
return System.identityHashCode(wrapped);
}
}
class SoftKeyReference extends SoftReference<Object> implements InternalReference {
final int hashCode;
SoftKeyReference(Object key) {
super(key, FinalizableReferenceQueue.getInstance());
this.hashCode = System.identityHashCode(key);
}
public void finalizeReferent() {
delegate.remove(this);
}
@Override
public int hashCode() {
return this.hashCode;
}
@Override
public boolean equals(Object o) {
return referenceEquals(this, o);
}
}
class WeakKeyReference extends WeakReference<Object> implements InternalReference {
final int hashCode;
WeakKeyReference(Object key) {
super(key, FinalizableReferenceQueue.getInstance());
this.hashCode = System.identityHashCode(key);
}
public void finalizeReferent() {
delegate.remove(this);
}
@Override
public int hashCode() {
return this.hashCode;
}
@Override
public boolean equals(Object o) {
return referenceEquals(this, o);
}
}
class SoftValueReference extends SoftReference<Object> implements InternalReference {
final Object keyReference;
SoftValueReference(Object keyReference, Object value) {
super(value, FinalizableReferenceQueue.getInstance());
this.keyReference = keyReference;
}
public void finalizeReferent() {
delegate.remove(keyReference, this);
}
@Override
public boolean equals(Object obj) {
return referenceEquals(this, obj);
}
}
class WeakValueReference extends WeakReference<Object> implements InternalReference {
final Object keyReference;
WeakValueReference(Object keyReference, Object value) {
super(value, FinalizableReferenceQueue.getInstance());
this.keyReference = keyReference;
}
public void finalizeReferent() {
delegate.remove(keyReference, this);
}
@Override
public boolean equals(Object obj) {
return referenceEquals(this, obj);
}
}
static class FinalizableReferenceQueue extends ReferenceQueue<Object> {
private FinalizableReferenceQueue() {
}
void cleanUp(Reference reference) {
try {
((InternalReference) reference).finalizeReferent();
} catch (Throwable t) {
throw new IllegalStateException("Unable to clean up after reference", t);
}
}
void start() {
Thread thread = new Thread("FinalizableReferenceQueue") {
@Override
@SuppressWarnings({ "InfiniteLoopStatement" })
public void run() {
while (true) {
try {
cleanUp(remove());
} catch (InterruptedException iex) { /* ignore */
}
}
}
};
thread.setDaemon(true);
thread.start();
}
static final ReferenceQueue<Object> instance = createAndStart();
static FinalizableReferenceQueue createAndStart() {
FinalizableReferenceQueue queue = new FinalizableReferenceQueue();
queue.start();
return queue;
}
/**
* Gets instance.
*/
public static ReferenceQueue<Object> getInstance() {
return instance;
}
}
// ---------------------------------------------------------------- put
// strategy
protected interface Strategy {
Object execute(ReferenceMap map, Object keyReference, Object valueReference);
}
private enum PutStrategy implements Strategy {
PUT {
public Object execute(ReferenceMap map, Object keyReference, Object valueReference) {
return map.dereferenceValue(map.delegate.put(keyReference, valueReference));
}
},
REPLACE {
public Object execute(ReferenceMap map, Object keyReference, Object valueReference) {
// ensure that the existing value is not collected
do {
Object existingValueReference;
Object existingValue;
do {
existingValueReference = map.delegate.get(keyReference);
existingValue = map.dereferenceValue(existingValueReference);
} while (isExpired(existingValueReference, existingValue));
if (existingValueReference == null) {
return Boolean.valueOf(false); // nothing to replace
}
if (map.delegate.replace(keyReference, existingValueReference, valueReference)) {
return existingValue; // existingValue did not expire
// since we still have a reference
// to it
}
} while (true);
}
},
PUT_IF_ABSENT {
public Object execute(ReferenceMap map, Object keyReference, Object valueReference) {
Object existingValueReference;
Object existingValue;
do {
existingValueReference = map.delegate.putIfAbsent(keyReference, valueReference);
existingValue = map.dereferenceValue(existingValueReference);
} while (isExpired(existingValueReference, existingValue));
return existingValue;
}
},
}
// ---------------------------------------------------------------- map
// entry set
class Entry implements Map.Entry<K, V> {
final K key;
V value;
Entry(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return this.key;
}
public V getValue() {
return this.value;
}
public V setValue(V newValue) {
value = newValue;
return put(key, newValue);
}
@Override
public int hashCode() {
return key.hashCode() * 31 + value.hashCode();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof ReferenceMap.Entry)) {
return false;
}
Entry entry = (Entry) o;
return key.equals(entry.key) && value.equals(entry.value);
}
@Override
public String toString() {
return key + "=" + value;
}
}
private volatile Set<Map.Entry<K, V>> entrySet;
@Override
public Set<Map.Entry<K, V>> entrySet() {
if (entrySet == null) {
entrySet = new EntrySet();
}
return entrySet;
}
private class EntrySet extends AbstractSet<Map.Entry<K, V>> {
@Override
public Iterator<Map.Entry<K, V>> iterator() {
return new ReferenceIterator();
}
@Override
public int size() {
return delegate.size();
}
@Override
public boolean contains(Object o) {
if (!(o instanceof Map.Entry)) {
return false;
}
Map.Entry<K, V> e = (Map.Entry<K, V>) o;
V v = ReferenceMap.this.get(e.getKey());
return v != null && v.equals(e.getValue());
}
@Override
public boolean remove(Object o) {
if (!(o instanceof Map.Entry)) {
return false;
}
Map.Entry<K, V> e = (Map.Entry<K, V>) o;
return ReferenceMap.this.remove(e.getKey(), e.getValue());
}
@Override
public void clear() {
delegate.clear();
}
}
private class ReferenceIterator implements Iterator<Map.Entry<K, V>> {
private Iterator<Map.Entry<Object, Object>> i = delegate.entrySet().iterator();
private Map.Entry<K, V> nextEntry;
private Map.Entry<K, V> lastReturned;
private ReferenceIterator() {
advanceToNext();
}
private void advanceToNext() {
while (i.hasNext()) {
Map.Entry<K, V> entry = dereferenceEntry(i.next());
if (entry != null) {
nextEntry = entry;
return;
}
}
nextEntry = null;
}
public boolean hasNext() {
return nextEntry != null;
}
public Map.Entry<K, V> next() {
if (nextEntry == null) {
throw new NoSuchElementException();
}
lastReturned = nextEntry;
advanceToNext();
return lastReturned;
}
public void remove() {
ReferenceMap.this.remove(lastReturned.getKey());
}
}
}
Loading…
Cancel
Save