精华帖 (6) :: 良好帖 (7) :: 新手帖 (0) :: 隐藏帖 (7)
|
|
---|---|
作者 | 正文 |
发表时间:2010-05-21
最后修改:2011-04-18
在非并发访问时,常用的为HashMap,在并发访问时,常用的是ConcurrentHashMap。但是如果我的键是在不没有强的引用时需要清理呢?那么可以使用WeakHashMap。如果键与值都要有引用效果呢?我推荐ConcurrentReferenceMap。什么?JDK没有这个类?好吧,确实没有,因为这个是我们自己写的。 WeakHashMap有个缺点,它的key在被回收后,不会从WeakHashMap中清除,而是要等下次调用 WeakHashMap的方法时才清除,这样就造成了不及时,所以也不是一个健康的机制。我们在写ConcurrentReferenceMap时,会用到的是 java.lang.ref包下的各个类来帮助我们做好清理工作。其中比较关键的为ReferenceQueue,要及时的清理,也就得依赖它了。 ReferenceQueue的remove方法会一直阻塞到有一个Reference被的对象被回收,然后返回这个对象。我们可以通过该机制来在资源被回收的时候就从Map中清除该Reference中对象的关联。那么首先,我们可以创建一个回调,在Reference中的对象被清理时执行该回调,并继承ReferenceQueue,实现我们需要调用回调的方法。 package cn.agrael.ref; /** * 带有回调清除功能的引用类型。 * @author <a href="http://www.agrael.cn">李翔</a> * * @param <T> 引用对象的类型 */ public interface FinalizableReference<T> { /** * 清除动作的回调方法。在该实现类包装的引用对象被垃圾回收器回收时调用该方法。 */ void finalizeReferent(); }
/** * 执行引用的清除工作。 * @param reference 要执行清除工作的引用。 */ void cleanUp(Reference<?> reference) { try { ((FinalizableReference<?>) reference).finalizeReferent(); } catch (Throwable t) { LOG.error("清除引用时发生错误",t); } }
/** * 开始垃圾回收引用监视。 */ void start() { Thread thread = new Thread("清除垃圾回收引用队列") { @Override public void run() { while (true) { try { cleanUp(remove()); } catch (InterruptedException e) { } } } }; thread.setDaemon(true); thread.start(); LOG.info("垃圾回收引用监视器开始工作。"); }
在准备好清理的机制以后,我们需要做的就是key与value各种引用的实现类,同时这些实现类要实现FinalizableReference接口,然后在实现的finalizeReferent方法中,清理自己的印射关系。
/** * 弱引用对象的键类。 * @author <a href="http://www.agrael.cn">李翔</a> * * @param <T> 引用类型 */ protected class FinalizableKeyWeakReference<T> extends FinalizableWeakReference<T> implements FinalizableReference<T>{ private final int hashCode; /** * 构造一个新的 弱引用对象的键类 实例。 * @param referent 键。 */ protected FinalizableKeyWeakReference(T referent) { super(referent); this.hashCode = keyHashCode(referent); } public void finalizeReferent() { map.remove(this); } @Override public int hashCode() { return hashCode; } @Override public boolean equals(Object object) { return referenceEquals(this, object); } } /** * 弱引用对象的值类。 * @author <a href="http://www.agrael.cn">李翔</a> * * @param <T> 引用类型 */ protected class FinalizableValueWeakReference<T> extends FinalizableWeakReference<T> implements FinalizableReference<T>{ private Object keyReference; /** * 构造一个新的 弱引用对象的值类 实例。 * @param keyReference 与此值关联的 键 。 * @param referent 值。 */ protected FinalizableValueWeakReference(Object keyReference ,T referent) { super(referent); this.keyReference = keyReference; } public void finalizeReferent() { map.remove(keyReference,this); } } /** * 软引用对象的键类。 * @author <a href="http://www.agrael.cn">李翔</a> * * @param <T> 引用类型 */ protected class FinalizableKeySoftReference<T> extends FinalizableSoftReference<T> implements FinalizableReference<T>{ private final int hashCode; /** * 构造一个新的 软引用对象的键类 实例。 * @param referent 键。 */ protected FinalizableKeySoftReference(T referent) { super(referent); this.hashCode = keyHashCode(referent); } public void finalizeReferent() { map.remove(this); } @Override public int hashCode() { return hashCode; } @Override public boolean equals(Object object) { return referenceEquals(this, object); } } /** * 软引用对象的值类。 * @author <a href="http://www.agrael.cn">李翔</a> * * @param <T> 引用类型 */ protected class FinalizableValueSoftReference<T> extends FinalizableSoftReference<T> implements FinalizableReference<T>{ private Object keyReference; /** * 构造一个新的 软引用对象的值类 实例。 * @param keyReference 与此值关联的 键 。 * @param referent 值。 */ protected FinalizableValueSoftReference(Object keyReference ,T referent) { super(referent); this.keyReference = keyReference; } public void finalizeReferent() { map.remove(keyReference,this); } } /** * 虚引用对象的键类。 * @author <a href="http://www.agrael.cn">李翔</a> * * @param <T> 引用类型 */ protected class FinalizableKeyPhantomReference<T> extends FinalizablePhantomReference<T> implements FinalizableReference<T>{ private final int hashCode; /** * 构造一个新的 虚引用对象的键类 实例。 * @param referent 键。 */ protected FinalizableKeyPhantomReference(T referent) { super(referent); this.hashCode = keyHashCode(referent); } public void finalizeReferent() { map.remove(this); } @Override public int hashCode() { return hashCode; } @Override public boolean equals(Object object) { return referenceEquals(this, object); } } /** * 虚引用对象的值类。 * @author <a href="http://www.agrael.cn">李翔</a> * * @param <T> 引用类型 */ protected class FinalizableValuePhantomReference<T> extends FinalizablePhantomReference<T> implements FinalizableReference<T>{ private Object keyReference; /** * 构造一个新的 虚引用对象的值类 实例。 * @param keyReference 与此值关联的 键 。 * @param referent 值。 */ protected FinalizableValuePhantomReference(Object keyReference ,T referent) { super(referent); this.keyReference = keyReference; } public void finalizeReferent() { map.remove(keyReference,this); } }
这些类实际被实现为ReferenceMap的内部类,以便获得ReferenceMap中的实例变量的引用。它们的回调方法中都为清除自身到Map的印射关联。
这个时候我们可以想象下,如果我们不是使用的强引用,而是使用弱引用、虚引用或者软引用其中之一,那么我们放入包装的Map中的不就是引用类型吗?这个时候如果我们使用key来查询的话,那我怎么知道是哪个呢?对于这个问题,我们可以提供2个包装器,在引用非强引用类型时,我们使用包装器试得它能与引用类型保持一样的行为,这样我们就能正确的找出 key 了。
/** * 引用类型中的对象的另一个包装器,该包装器实现为引用类型中的对象保持 put 与 get 时用到的 hashCode 和 equals 方法的一致性。 * @author <a href="http://www.agrael.cn">李翔</a> * */ protected static class ReferenceWrapper{ protected final Object reference; /** * 使用一个引用类型中的对象实例构造一个新的包装器。 * @param reference 要包装的引用类型中的对象实例。 */ protected ReferenceWrapper(Object reference) { this.reference = reference; } /** * 得到被包装的对象。 * @return 被包装的对象。 */ protected Object getReference() { return reference; } /** * 调用包装对象的 hashCode 方法。 * @return 包装对象的 hashCode。 */ @Override public int hashCode() { return reference.hashCode(); } /** * 调用包装对象的 equals 方法比较。 * @param obj 要与之比较的对象。 * @return 如果传入的对象和包装对象“相等”则返回 true,否则返回 false。 */ @Override public boolean equals(Object obj) { // 使用包装对象的 equals return reference.equals(obj); } } /** * 专用于 key 的引用类型中的对象的另一个包装器。该包装器重写了 hashCode 方法,使用 {@link System#identityHashCode(Object)} 取得 hash值 。 * @author <a href="http://www.agrael.cn">李翔</a> * */ protected static class KeyReferenceWrapper extends ReferenceWrapper { /** * 使用一个引用类型中的 key对象实例 构造一个新的包装器。 * @param reference 要包装的引用类型中的key对象实例。 */ protected KeyReferenceWrapper(Object reference) { super(reference); } /** * 返回包装的 Key 的内存里的 hashCode。 * @return 包装的 Key 的内存里的 hashCode。 * @see System#identityHashCode(Object) */ @Override public int hashCode() { // 这里要认为同一内存地址的才是相同对象,所以取得内存地址中的 HASH码 return System.identityHashCode(reference); } }
同样,这2个类也为内部类,因为要得到ConcurrentReferenceMap实例变量的引用。
到了这里,我们的清理与包装都成型了,最后就差完成ConcurrentReferenceMap了。
最后上ConcurrentReferenceMap的代码:
package cn.agrael.collection; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.ref.Reference; import java.util.AbstractMap; import java.util.AbstractSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import cn.agrael.ref.FinalizablePhantomReference; import cn.agrael.ref.FinalizableReference; import cn.agrael.ref.FinalizableReferenceQueue; import cn.agrael.ref.FinalizableSoftReference; import cn.agrael.ref.FinalizableWeakReference; /** * 支持检索的完全并发和更新的所期望可调整并发并且键值对支持引用类型的哈希表。尽管所有操作都是线程安全的,但检索操作不 必锁定,并且不 * 支持以某种防止所有访问的方式锁定整个表。 * <p> * 检索操作(包括 get)通常不会受阻塞,因此,可能与更新操作交迭(包括 put 和 remove)。检索会影响最近完成的 * 更新操作的结果。对于一些聚合操作,比如 putAll 和 * clear,并发检索可能只影响某些条目的插入和移除。类似地,在创建迭代器/枚举时或自此之后,Iterators 和 Enumerations * 返回在某一时间点上影响哈希表状态的元素。它们不会 抛出 * ConcurrentModificationException。不过,迭代器被设计成每次仅由一个线程使用。 * </p> * <p> * 这允许通过可选的 concurrencyLevel 构造方法参数(默认值为 * 16)来引导更新操作之间的并发,该参数用作内部调整大小的一个提示。表是在内部进行分区的 * ,试图允许指示无争用并发更新的数量。因为哈希表中的位置基本上是随意的, * 所以实际的并发将各不相同。理想情况下,应该选择一个尽可能多地容纳并发修改该表的线程的值 * 。使用一个比所需要的值高很多的值可能会浪费空间和时间,而使用一个显然低很多的值可能导致线程争用 * 。对数量级估计过高或估计过低通常都会带来非常显著的影响。当仅有一个线程将执行修改操作 * ,而其他所有线程都只是执行读取操作时,才认为某个值是合适的。此外,重新调整此类或其他任何种类哈希表的大小都是一个相对较慢的操作 * ,因此,在可能的时候,提供构造方法中期望表大小的估计值是一个好主意。 * </p> * <p> * 引用类型请参考 {@link java.lang.ref}与 {@link cn.agrael.ref} 的描述。 * </p> * <p> * 在第一次调用 put/putAll 方法时(仅仅是在同一个类加载器中的第一次 * ,如果为同一类加载器,那么在put/putAll后,新创建的实例的put/putAll也不算做第一次),会有一个后台线程随之创建并启动 * ,该后台线程主要作用和垃圾回收交互,进行回收对象的清理。在清理 键 的同时,会一同清理掉 键的引用包装器 以及 值 和 值的引用包装器 以及 印射 * ,在清理 值 时 ,会一同清理掉 值的引用包装器,然后从 Map 中移除印射到自身的 键 以及 印射 。 * </p> * <p> * <strong>该清理是 异步 且 “即时” 的,会在垃圾回收的同时清理,不需要在下次对该 Map 操作时才清理包装引用的实例。</strong> * </p> * <p> * 此类及其视图和迭代器实现了 {@link ConcurrentMap} 和 {@link Iterator} 接口的所有可选 方法。 * </p> * <p> * 该实现不支持参数为 null。如果传入 null 则会抛出 {@link NullPointerException}。 * </p> * * @author <a href="http://www.agrael.cn">李翔</a> * * @param <K> * 此映射维护的键类型 * @param <V> * 映射值的类型 */ public class ConcurrentReferenceMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>, Serializable { /** 版本号 */ private static final long serialVersionUID = -6254917146703229097L; private static final FinalizableReferenceQueue FINALIZABLE_REFERENCE_QUEUE = new FinalizableReferenceQueue( "ConcurrentReferenceMapReferenceQueue"); private final ReferenceKeyType keyReferenceType; private final ReferenceValueType valueReferenceType; private transient int initialCapacity = 16; private transient float loadFactor = 0.75f; private transient int concurrencyLevel = 16; private transient ConcurrentMap<Object, Object> map; /** * 通过指定的 key引用类型 和 value引用类型 创建一个带有默认初始容量、加载因子和 concurrencyLevel 的新的空映射。 * @param keyReferenceType key引用类型 。 * @param valueReferenceType value引用类型。 */ public ConcurrentReferenceMap(ReferenceKeyType keyReferenceType, ReferenceValueType valueReferenceType) { this.keyReferenceType = keyReferenceType; this.valueReferenceType = valueReferenceType; this.map = new ConcurrentHashMap<Object, Object>(this.initialCapacity, this.loadFactor, this.concurrencyLevel); } /** * 通过指定的 key引用类型 和 value引用类型 创建一个带有指定初始容量、默认加载因子和 concurrencyLevel 的新的空映射。 * @param keyReferenceType key引用类型。 * @param valueReferenceType value引用类型。 * @param initialCapacity 初始容量。该实现执行内部大小调整,以容纳这些元素。 */ public ConcurrentReferenceMap(ReferenceKeyType keyReferenceType, ReferenceValueType valueReferenceType, int initialCapacity) { this.keyReferenceType = keyReferenceType; this.valueReferenceType = valueReferenceType; this.initialCapacity = initialCapacity; this.map = new ConcurrentHashMap<Object, Object>(initialCapacity, this.loadFactor, this.concurrencyLevel); } /** * 通过指定的 key引用类型 和 value引用类型 创建一个带有指定初始容量、加载因子和并发级别的新的空映射。 * @param keyReferenceType key引用类型。 * @param valueReferenceType value引用类型。 * @param initialCapacity 初始容量。该实现执行内部大小调整,以容纳这些元素。 * @param loadFactor 加载因子阈值,用来控制重新调整大小。在每 bin 中的平均元素数大于此阈值时,可能要重新调整大小。 * @param concurrencyLevel 当前更新线程的估计数。该实现将执行内部大小调整,以尽量容纳这些线程。 */ public ConcurrentReferenceMap(ReferenceKeyType keyReferenceType, ReferenceValueType valueReferenceType, int initialCapacity, float loadFactor, int concurrencyLevel) { this.keyReferenceType = keyReferenceType; this.valueReferenceType = valueReferenceType; this.concurrencyLevel = concurrencyLevel; this.initialCapacity = initialCapacity; this.loadFactor = loadFactor; this.map = new ConcurrentHashMap<Object, Object>(initialCapacity, loadFactor, concurrencyLevel); } @SuppressWarnings("unchecked") @Override public Set<K> keySet() { if (this.keyReferenceType == ReferenceKeyType.STRONG) { // 如果为强引用,则 map 中的 keySet 就是需要的 return (Set<K>) this.map.keySet(); } // 为其他引用 return new AbstractSet<K>() { public Iterator<K> iterator() { return new Iterator<K>() { private Iterator<Object> iterator = ConcurrentReferenceMap.this.map.keySet().iterator(); public boolean hasNext() { return this.iterator.hasNext(); } public K next() { return getKey(this.iterator.next()); } public void remove() { this.iterator.remove(); } }; } public int size() { return ConcurrentReferenceMap.this.map.size(); } }; } @Override public void clear() { this.map.clear(); } @Override public V remove(Object key) { notNull(key); Object keyReference = createKeyReferenceWrapper(key); Object returnValueReference = this.map.remove(keyReference); return getValue(returnValueReference); } @Override public boolean containsKey(Object key) { return this.map.containsKey(key); } @Override public int size() { return this.map.size(); } @Override public boolean isEmpty() { return this.map.isEmpty(); } @SuppressWarnings("unchecked") private V getValue(Object valueReference) { return (V) (this.valueReferenceType == ReferenceValueType.STRONG ? valueReference : valueReference == null ? null : ((Reference<V>) valueReference).get()); } @SuppressWarnings("unchecked") private K getKey(Object keyReference) { return (K) (this.keyReferenceType == ReferenceKeyType.STRONG ? keyReference : keyReference == null ? null : ((Reference<K>) keyReference).get()); } /** * 创建 Key 的印射包装器。 * @param key 要创建印射包装器的 key。 * @return 如果是强引用,返回 key 本身,否则返回 Key 的印射包装器。 */ private Object createKeyReferenceWrapper(Object key) { return this.keyReferenceType == ReferenceKeyType.STRONG ? key : new KeyReferenceWrapper(key); } /** * 创建 Value 的印射包装器。 * @param value 要创建印射包装器的 value。 * @return 如果是强引用,返回 value 本身,否则返回 Value 的印射包装器。 */ private Object createValueReferenceWrapper(Object value) { return this.valueReferenceType == ReferenceValueType.STRONG ? value : new ValueReferenceWrapper(value); } /** * 得到 key 在内存中的地址的 hashCode。 * @param key 要得到在内存中的地址的 hashCode 的 key。 * @return key 在内存中的地址的 hashCode。 * @see System#identityHashCode(Object) */ private int keyHashCode(Object key) { return System.identityHashCode(key); } private boolean referenceEquals(Reference<?> reference, Object object) { if (reference == object) { // 直接引用相同 return true; } if (reference == null) { // 一方为 null 则返回 false(因为上个判断结合这个判断,object 一定不为 null) return false; } if (object instanceof Reference<?>) { // object 为引用类型,则比较引用类型中的值的引用 Object referenceValue = ((Reference<?>) object).get(); return referenceValue != null && reference.get() == referenceValue; } if (object instanceof ReferenceWrapper) { // object为 引用包装类型 return ((ReferenceWrapper) object).getReference() == reference.get(); } return false; } @Override public V get(Object key) { notNull(key); Object keyReference = createKeyReferenceWrapper(key); Object returnValueReference = this.map.get(keyReference); return getValue(returnValueReference); } @Override public V put(K key, V value) { notNull(key); notNull(value); Object keyReference = wrapKey(key); Object valueReference = wrapValue(keyReference, value); Object returnValueReference = this.map.put(keyReference, valueReference); if (returnValueReference == null) { return null; } return getValue(returnValueReference); } public V putIfAbsent(K key, V value) { notNull(key); notNull(value); Object keyReference = wrapKey(key); Object valueReference = wrapValue(keyReference, value); Object returnValueReference = this.map.putIfAbsent(keyReference, valueReference); if (returnValueReference == null) { return null; } return getValue(returnValueReference); } public boolean remove(Object key, Object value) { notNull(key); Object keyReference = createKeyReferenceWrapper(key); Object valueReference = createValueReferenceWrapper(value); return this.map.remove(keyReference, valueReference); } public boolean replace(K key, V oldValue, V newValue) { notNull(key); notNull(oldValue); notNull(newValue); Object keyReference = wrapKey(key); Object oldValueReference = createValueReferenceWrapper(oldValue); Object valueReference = wrapValue(keyReference, newValue); return this.map.replace(keyReference, oldValueReference, valueReference); } public V replace(K key, V value) { notNull(key); notNull(value); Object keyReference = wrapKey(key); Object valueReference = wrapValue(keyReference, value); Object returnValueReference = this.map.replace(keyReference, valueReference); if (returnValueReference == null) { return null; } return getValue(returnValueReference); } private Object wrapKey(K key) { Object keyReference = null; switch (this.keyReferenceType) { case STRONG: keyReference = key; break; case SOFT: keyReference = new FinalizableKeySoftReference<K>(key); break; case WEAK: keyReference = new FinalizableKeyWeakReference<K>(key); break; case PHANTOM: keyReference = new FinalizableKeyPhantomReference<K>(key); break; } return keyReference; } private Object wrapValue(Object keyReference, V value) { Object valueReference = null; switch (this.valueReferenceType) { case STRONG: valueReference = value; break; case SOFT: valueReference = new FinalizableValueSoftReference<V>(keyReference ,value); break; case WEAK: valueReference = new FinalizableValueWeakReference<V>(keyReference ,value); break; case PHANTOM: valueReference = new FinalizableValuePhantomReference<V>(keyReference ,value); break; } return valueReference; } public Set<Entry<K, V>> entrySet() { return new AbstractSet<Entry<K, V>>() { public Iterator<Entry<K, V>> iterator() { return new Iterator<Entry<K, V>>() { private Iterator<Entry<Object, Object>> iterator = ConcurrentReferenceMap.this.map.entrySet().iterator(); public boolean hasNext() { return this.iterator.hasNext(); } public Entry<K, V> next() { final Entry<Object, Object> entry = this.iterator.next(); return new Entry<K, V>() { public K getKey() { return ConcurrentReferenceMap.this.getKey(entry.getKey()); } public V getValue() { return ConcurrentReferenceMap.this.getValue(entry.getValue()); } public V setValue(V value) { throw new UnsupportedOperationException("不支持的操作。"); } }; } public void remove() { this.iterator.remove(); } }; } public int size() { return ConcurrentReferenceMap.this.map.size(); } }; } /** * 弱引用对象的键类。 * @author <a href="http://www.agrael.cn">李翔</a> * * @param <T> 引用类型 */ protected class FinalizableKeyWeakReference<T> extends FinalizableWeakReference<T> implements FinalizableReference<T> { private final int hashCode; /** * 构造一个新的 弱引用对象的键类 实例。 * @param referent 键。 */ protected FinalizableKeyWeakReference(T referent) { super(referent, FINALIZABLE_REFERENCE_QUEUE); this.hashCode = keyHashCode(referent); } public void finalizeReferent() { ConcurrentReferenceMap.this.map.remove(this); } @Override public int hashCode() { return this.hashCode; } @Override public boolean equals(Object object) { return referenceEquals(this, object); } } /** * 弱引用对象的值类。 * @author <a href="http://www.agrael.cn">李翔</a> * * @param <T> 引用类型 */ protected class FinalizableValueWeakReference<T> extends FinalizableWeakReference<T> implements FinalizableReference<T> { private Object keyReference; /** * 构造一个新的 弱引用对象的值类 实例。 * @param keyReference 与此值关联的 键 。 * @param referent 值。 */ protected FinalizableValueWeakReference(Object keyReference, T referent) { super(referent, FINALIZABLE_REFERENCE_QUEUE); this.keyReference = keyReference; } public void finalizeReferent() { ConcurrentReferenceMap.this.map.remove(this.keyReference, this); } @Override public int hashCode() { T v = get(); return v == null ? 0 : v.hashCode(); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (this == obj) { return true; } if (!(obj instanceof ConcurrentReferenceMap<?, ?>.FinalizableValueWeakReference<?>)) { return false; } T v = get(); Object objV = ((FinalizableValueWeakReference<?>) obj).get(); if (v == null) { if (v == objV) { return true; } } return v.equals(objV); } } /** * 软引用对象的键类。 * @author <a href="http://www.agrael.cn">李翔</a> * * @param <T> 引用类型 */ protected class FinalizableKeySoftReference<T> extends FinalizableSoftReference<T> implements FinalizableReference<T> { private final int hashCode; /** * 构造一个新的 软引用对象的键类 实例。 * @param referent 键。 */ protected FinalizableKeySoftReference(T referent) { super(referent, FINALIZABLE_REFERENCE_QUEUE); this.hashCode = keyHashCode(referent); } public void finalizeReferent() { ConcurrentReferenceMap.this.map.remove(this); } @Override public int hashCode() { return this.hashCode; } @Override public boolean equals(Object object) { return referenceEquals(this, object); } } /** * 软引用对象的值类。 * @author <a href="http://www.agrael.cn">李翔</a> * * @param <T> 引用类型 */ protected class FinalizableValueSoftReference<T> extends FinalizableSoftReference<T> implements FinalizableReference<T> { private Object keyReference; /** * 构造一个新的 软引用对象的值类 实例。 * @param keyReference 与此值关联的 键 。 * @param referent 值。 */ protected FinalizableValueSoftReference(Object keyReference ,T referent) { super(referent, FINALIZABLE_REFERENCE_QUEUE); this.keyReference = keyReference; } public void finalizeReferent() { ConcurrentReferenceMap.this.map.remove(this.keyReference, this); } @Override public int hashCode() { T v = get(); return v == null ? 0 : v.hashCode(); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (this == obj) { return true; } if (!(obj instanceof ConcurrentReferenceMap<?, ?>.FinalizableValueSoftReference<?>)) { return false; } T v = get(); Object objV = ((FinalizableValueSoftReference<?>) obj).get(); if (v == null) { if (v == objV) { return true; } } return v.equals(objV); } } /** * 虚引用对象的键类。 * @author <a href="http://www.agrael.cn">李翔</a> * * @param <T> 引用类型 */ protected class FinalizableKeyPhantomReference<T> extends FinalizablePhantomReference<T> implements FinalizableReference<T> { private final int hashCode; /** * 构造一个新的 虚引用对象的键类 实例。 * @param referent 键。 */ protected FinalizableKeyPhantomReference(T referent) { super(referent, FINALIZABLE_REFERENCE_QUEUE); this.hashCode = keyHashCode(referent); } public void finalizeReferent() { ConcurrentReferenceMap.this.map.remove(this); } @Override public int hashCode() { return this.hashCode; } @Override public boolean equals(Object object) { return referenceEquals(this, object); } } /** * 虚引用对象的值类。 * @author <a href="http://www.agrael.cn">李翔</a> * * @param <T> 引用类型 */ protected class FinalizableValuePhantomReference<T> extends FinalizablePhantomReference<T> implements FinalizableReference<T> { private Object keyReference; /** * 构造一个新的 虚引用对象的值类 实例。 * @param keyReference 与此值关联的 键 。 * @param referent 值。 */ protected FinalizableValuePhantomReference(Object keyReference ,T referent) { super(referent, FINALIZABLE_REFERENCE_QUEUE); this.keyReference = keyReference; } public void finalizeReferent() { ConcurrentReferenceMap.this.map.remove(this.keyReference, this); } @Override public int hashCode() { return 0; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (this == obj) { return true; } if (!(obj instanceof ConcurrentReferenceMap<?, ?>.FinalizableValuePhantomReference<?>)) { return false; } Object v = ((FinalizableValuePhantomReference<?>) obj).get(); if (v == null) { return true; } return v.equals(get()); } } /** * 引用类型中的对象的另一个包装器,该包装器实现为引用类型中的对象保持 put 与 get 时用到的 hashCode 和 equals 方法的一致性。 * @author <a href="http://www.agrael.cn">李翔</a> * */ protected static class ReferenceWrapper { protected final Object reference; /** * 使用一个引用类型中的对象实例构造一个新的包装器。 * @param reference 要包装的引用类型中的对象实例。 */ protected ReferenceWrapper(Object reference) { this.reference = reference; } /** * 得到被包装的对象。 * @return 被包装的对象。 */ protected Object getReference() { return this.reference; } /** * 调用包装对象的 hashCode 方法。 * @return 包装对象的 hashCode。 */ @Override public int hashCode() { return this.reference.hashCode(); } /** * 调用包装对象的 equals 方法比较。 * @param obj 要与之比较的对象。 * @return 如果传入的对象和包装对象“相等”则返回 true,否则返回 false。 */ @Override public boolean equals(Object obj) { // 使用包装对象的 equals return obj.equals(this); } } /** * 专用于 key 的引用类型中的对象的另一个包装器。该包装器重写了 hashCode 方法,使用 {@link System#identityHashCode(Object)} 取得 hash值 。 * @author <a href="http://www.agrael.cn">李翔</a> * */ protected static class KeyReferenceWrapper extends ReferenceWrapper { /** * 使用一个引用类型中的 key对象实例 构造一个新的包装器。 * @param reference 要包装的引用类型中的key对象实例。 */ protected KeyReferenceWrapper(Object reference) { super(reference); } /** * 返回包装的 Key 的内存里的 hashCode。 * @return 包装的 Key 的内存里的 hashCode。 * @see System#identityHashCode(Object) */ @Override public int hashCode() { // 这里要认为同一内存地址的才是相同对象,所以取得内存地址中的 HASH码 return System.identityHashCode(this.reference); } } /** * 专用于 value 的引用类型中的对象的另一个包装器。该包装器重写了 hashCode 方法,使用 {@link System#identityHashCode(Object)} 取得 hash值 。 * @author <a href="http://www.agrael.cn">李翔</a> * */ protected static class ValueReferenceWrapper extends ReferenceWrapper { /** * 使用一个引用类型中的 key对象实例 构造一个新的包装器。 * @param reference 要包装的引用类型中的key对象实例。 */ protected ValueReferenceWrapper(Object reference) { super(reference); } } /** * 用作 K 的引用类型。 * @author <a href="http://www.agrael.cn">李翔</a> * */ public static enum ReferenceKeyType { /** 强引用 */ STRONG, /** 软引用 */ SOFT, /** 弱引用 */ WEAK, /** 虚引用 */ PHANTOM; } /** * 用作 V 的引用类型。 * @author <a href="http://www.agrael.cn">李翔</a> * */ public static enum ReferenceValueType { /** 强引用 */ STRONG, /** 软引用 */ SOFT, /** 弱引用 */ WEAK, /** 虚引用 */ PHANTOM; } static void notNull(Object obj) { if (obj == null) { throw new NullPointerException(); } } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeInt(size()); out.writeFloat(this.loadFactor); out.writeInt(this.concurrencyLevel); out.writeInt(this.initialCapacity); for (Map.Entry<Object, Object> entry : this.map.entrySet()) { Object key = getKey(entry.getKey()); Object value = getValue(entry.getValue()); if (key != null && value != null) { out.writeObject(key); out.writeObject(value); } } out.writeObject(null); } @SuppressWarnings("unchecked") private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); int size = in.readInt(); float loadFactor = in.readFloat(); int concurrencyLevel = in.readInt(); int initialCapacity = in.readInt(); this.loadFactor = loadFactor; this.concurrencyLevel = concurrencyLevel; this.initialCapacity = initialCapacity; this.map = new ConcurrentHashMap<Object, Object>(size, loadFactor, concurrencyLevel); while (true) { K key = (K) in.readObject(); if (key == null) { break; } V value = (V) in.readObject(); put(key, value); } } }
其中最后那2个枚举为ConcurrentReferenceMap的 key 与 value 引用类型的枚举,在构造 ConcurrentReferenceMap时,使用该枚举即可创建想要的引用类型的ConcurrentReferenceMap。
以下是一个示例:
public static void main(String[] args) throws Exception { ConcurrentMap<Object, Object> referenceMap = new ConcurrentReferenceMap<Object, Object>(ReferenceKeyType.WEAK, ReferenceValueType.WEAK); Object key = new Object(); Object value = new Object(); referenceMap.put(key, value); System.out.println(referenceMap.isEmpty()); // 返回 false // 由于是弱引用,所以将 key 或者 value 其中某一个 设置为 null 都可触发清除 key = null; // 将 key 消除强引用 //value = null; // 或者注释上面一行将该行取消注释,将 value 消除强引用 System.gc(); TimeUnit.SECONDS.sleep(5); System.out.println(referenceMap.isEmpty()); // 返回true,证明已经被清除,这期间不需要像WeakHashMap一样操作后才清除,并且保证并发安全 }
最后完整的代码在附件里。
现在已经修改为实现ConcurrentMap接口。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-05-21
|
|
返回顶楼 | |
发表时间:2010-05-21
用一个线程去做清理工作是不是太 heavy-weight 了
此外我不明白什么叫做 value 也需要引用?我好奇有这种场景应用么? |
|
返回顶楼 | |
发表时间:2010-05-21
无论创建多少个ReferenceMap的实例都只有一个守护线程并不觉得heavy-weight。
value的引用会有这样的场景。例如Class |
|
返回顶楼 | |
发表时间:2010-05-21
首先楼主这种精神值得表扬。
如果我没有理解错的话,你的思路就是,Daemon线程在不停“GC”(你这里的finalizeReferent)非强引用,不过JVM启动的时候,有一个Finalizer的线程在执行。 这里有几个问题: 1.三种非强引用的生命周期不同,软引用是在内存不足的情况被清理,弱引用是在对象不在关联,幻想的话,是已经被GC掉了。 2.ReferenceMap实现中,保存全部的是强引用对象,程序中并不能确定合适清理。 3.因为第2点,会产生内存泄漏。 4.命名,加一个Concurrent吧。 |
|
返回顶楼 | |
发表时间:2010-05-21
mercyblitz 写道 首先楼主这种精神值得表扬。
如果我没有理解错的话,你的思路就是,Daemon线程在不停“GC”(你这里的finalizeReferent)非强引用,不过JVM启动的时候,有一个Finalizer的线程在执行。 这里有几个问题: 1.三种非强引用的生命周期不同,软引用是在内存不足的情况被清理,弱引用是在对象不在关联,幻想的话,是已经被GC掉了。 2.ReferenceMap实现中,保存全部的是强引用对象,程序中并不能确定合适清理。 3.因为第2点,会产生内存泄漏。 4.命名,加一个Concurrent吧。 首先感谢你对帖子的关注。 1、是的,已经被GC清理掉了,但是在清理后,一直阻塞的ReferenceQueue的remove方法会返回当前被清理的对象的Reference的引用。实际这个时候Reference里的对象已经为null(被清理)。但是在这里我们并不会用到那个对象,而是使用的实现了FinalizableReference的Reference的清理方法finalizeReferent。以FinalizableKeyWeakReference来说,它的finalizeReferent方法为: public void finalizeReferent() { map.remove(this); } 可以看到,在这里已经从map中移除了自身在包装的ConcurrentHashMap中的印射关系。 2、由于1的结论,在key/value的对象被清理时,实际已经从该Map中清除关系了。 3、由于回答2的原因,内存泄漏问题得到解决。 4、谢谢这个,非常好的提议:) |
|
返回顶楼 | |
发表时间:2010-05-21
Agrael 写道 mercyblitz 写道 首先楼主这种精神值得表扬。
如果我没有理解错的话,你的思路就是,Daemon线程在不停“GC”(你这里的finalizeReferent)非强引用,不过JVM启动的时候,有一个Finalizer的线程在执行。 这里有几个问题: 1.三种非强引用的生命周期不同,软引用是在内存不足的情况被清理,弱引用是在对象不在关联,幻想的话,是已经被GC掉了。 2.ReferenceMap实现中,保存全部的是强引用对象,程序中并不能确定合适清理。 3.因为第2点,会产生内存泄漏。 4.命名,加一个Concurrent吧。 首先感谢你对帖子的关注。 1、是的,已经被GC清理掉了,但是在清理后,一直阻塞的ReferenceQueue的remove方法会返回当前被清理的对象的Reference的引用。实际这个时候Reference里的对象已经为null(被清理)。但是在这里我们并不会用到那个对象,而是使用的实现了FinalizableReference的Reference的清理方法finalizeReferent。以FinalizableKeyWeakReference来说,它的finalizeReferent方法为: public void finalizeReferent() { map.remove(this); } 可以看到,在这里已经从map中移除了自身在包装的ConcurrentHashMap中的印射关系。 2、由于1的结论,在key/value的对象被清理时,实际已经从该Map中清除关系了。 3、由于回答2的原因,内存泄漏问题得到解决。 4、谢谢这个,非常好的提议:) 你的东西是一个很好的实践,为什么我这么说? 当年Doug Lea写的java.util.concurrent.locks.AbstractQueuedSynchronizer 能够通过编程的方法实现synchronized,虽然离不开JVM的本地方法,但是也是一种很好的设计。 题外话,我很欣赏楼主,呵呵,因为我也这样的人,喜欢重复发明轮子,为什么不能? |
|
返回顶楼 | |
发表时间:2010-05-24
mercyblitz 写道 Agrael 写道 mercyblitz 写道 首先楼主这种精神值得表扬。
如果我没有理解错的话,你的思路就是,Daemon线程在不停“GC”(你这里的finalizeReferent)非强引用,不过JVM启动的时候,有一个Finalizer的线程在执行。 这里有几个问题: 1.三种非强引用的生命周期不同,软引用是在内存不足的情况被清理,弱引用是在对象不在关联,幻想的话,是已经被GC掉了。 2.ReferenceMap实现中,保存全部的是强引用对象,程序中并不能确定合适清理。 3.因为第2点,会产生内存泄漏。 4.命名,加一个Concurrent吧。 首先感谢你对帖子的关注。 1、是的,已经被GC清理掉了,但是在清理后,一直阻塞的ReferenceQueue的remove方法会返回当前被清理的对象的Reference的引用。实际这个时候Reference里的对象已经为null(被清理)。但是在这里我们并不会用到那个对象,而是使用的实现了FinalizableReference的Reference的清理方法finalizeReferent。以FinalizableKeyWeakReference来说,它的finalizeReferent方法为: public void finalizeReferent() { map.remove(this); } 可以看到,在这里已经从map中移除了自身在包装的ConcurrentHashMap中的印射关系。 2、由于1的结论,在key/value的对象被清理时,实际已经从该Map中清除关系了。 3、由于回答2的原因,内存泄漏问题得到解决。 4、谢谢这个,非常好的提议:) 你的东西是一个很好的实践,为什么我这么说? 当年Doug Lea写的java.util.concurrent.locks.AbstractQueuedSynchronizer 能够通过编程的方法实现synchronized,虽然离不开JVM的本地方法,但是也是一种很好的设计。 题外话,我很欣赏楼主,呵呵,因为我也这样的人,喜欢重复发明轮子,为什么不能? 呵呵,其实我还比较喜欢发明轮子的。其实看看现在的开源框架,是否一个又一个轮子呢? |
|
返回顶楼 | |
发表时间:2010-05-24
Agrael 写道 mercyblitz 写道 Agrael 写道 mercyblitz 写道 首先楼主这种精神值得表扬。
如果我没有理解错的话,你的思路就是,Daemon线程在不停“GC”(你这里的finalizeReferent)非强引用,不过JVM启动的时候,有一个Finalizer的线程在执行。 这里有几个问题: 1.三种非强引用的生命周期不同,软引用是在内存不足的情况被清理,弱引用是在对象不在关联,幻想的话,是已经被GC掉了。 2.ReferenceMap实现中,保存全部的是强引用对象,程序中并不能确定合适清理。 3.因为第2点,会产生内存泄漏。 4.命名,加一个Concurrent吧。 首先感谢你对帖子的关注。 1、是的,已经被GC清理掉了,但是在清理后,一直阻塞的ReferenceQueue的remove方法会返回当前被清理的对象的Reference的引用。实际这个时候Reference里的对象已经为null(被清理)。但是在这里我们并不会用到那个对象,而是使用的实现了FinalizableReference的Reference的清理方法finalizeReferent。以FinalizableKeyWeakReference来说,它的finalizeReferent方法为: public void finalizeReferent() { map.remove(this); } 可以看到,在这里已经从map中移除了自身在包装的ConcurrentHashMap中的印射关系。 2、由于1的结论,在key/value的对象被清理时,实际已经从该Map中清除关系了。 3、由于回答2的原因,内存泄漏问题得到解决。 4、谢谢这个,非常好的提议:) 你的东西是一个很好的实践,为什么我这么说? 当年Doug Lea写的java.util.concurrent.locks.AbstractQueuedSynchronizer 能够通过编程的方法实现synchronized,虽然离不开JVM的本地方法,但是也是一种很好的设计。 题外话,我很欣赏楼主,呵呵,因为我也这样的人,喜欢重复发明轮子,为什么不能? 呵呵,其实我还比较喜欢发明轮子的。其实看看现在的开源框架,是否一个又一个轮子呢? 勇于创造,才有创新,凭空不能想象出伟大。 |
|
返回顶楼 | |
发表时间:2010-05-26
mercyblitz 写道 Agrael 写道 mercyblitz 写道 Agrael 写道 mercyblitz 写道 首先楼主这种精神值得表扬。 如果我没有理解错的话,你的思路就是,Daemon线程在不停“GC”(你这里的finalizeReferent)非强引用,不过JVM启动的时候,有一个Finalizer的线程在执行。 这里有几个问题: 1.三种非强引用的生命周期不同,软引用是在内存不足的情况被清理,弱引用是在对象不在关联,幻想的话,是已经被GC掉了。 2.ReferenceMap实现中,保存全部的是强引用对象,程序中并不能确定合适清理。 3.因为第2点,会产生内存泄漏。 4.命名,加一个Concurrent吧。 首先感谢你对帖子的关注。 1、是的,已经被GC清理掉了,但是在清理后,一直阻塞的ReferenceQueue的remove方法会返回当前被清理的对象的Reference的引用。实际这个时候Reference里的对象已经为null(被清理)。但是在这里我们并不会用到那个对象,而是使用的实现了FinalizableReference的Reference的清理方法finalizeReferent。以FinalizableKeyWeakReference来说,它的finalizeReferent方法为: public void finalizeReferent() { map.remove(this); } 可以看到,在这里已经从map中移除了自身在包装的ConcurrentHashMap中的印射关系。 2、由于1的结论,在key/value的对象被清理时,实际已经从该Map中清除关系了。 3、由于回答2的原因,内存泄漏问题得到解决。 4、谢谢这个,非常好的提议:) 你的东西是一个很好的实践,为什么我这么说? 当年Doug Lea写的java.util.concurrent.locks.AbstractQueuedSynchronizer 能够通过编程的方法实现synchronized,虽然离不开JVM的本地方法,但是也是一种很好的设计。 题外话,我很欣赏楼主,呵呵,因为我也这样的人,喜欢重复发明轮子,为什么不能? 呵呵,其实我还比较喜欢发明轮子的。其实看看现在的开源框架,是否一个又一个轮子呢? 勇于创造,才有创新,凭空不能想象出伟大。 这句话太有哲理性了,朋友也是喜欢写自己的东西的,不知道对JAAS的了解如何?我想探讨下关于JAAS在应用服务器中自定义LoginModule的问题。 |
|
返回顶楼 | |