ConcurrentReferenceHashMap
官方描述:一個Concurrent的HashMap,對鍵和值使用軟引用或弱引用。在并發通路時支援更好的性能,可用作Collections.synchronizedMap(new WeakHashMap<K,Reference<V>>())的替代品。此實作遵循與ConcurrentHashMap相同的設計限制,支援null的key或value。
從以上描述可以看出,這個類是WeakHashMap的Concurrent版,并且支援SoftReference和WeakReference(預設是SoftReference,可以通過構造方法指定)
既然可用作Collections.synchronizedMap(new WeakHashMap<K,Reference<V>>())的替代品,就要先介紹一下WeakHashMap。
WeakHashMap
Entry<K,V>[] table; 其Entry繼承了WeakReference
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
在使用put(K,V)時,就會使用K,V,ReferenceQueue等參數構造Entry,是以GC時Entry就會進入queue
在使用get(K)、size() 等方法時,就會觸發private void expungeStaleEntries() 使用queue.poll()識别哪些Entry已經被回收,重新整理table達到資料是實時的效果
private static final int DEFAULT_INITIAL_CAPACITY = 16;
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
private static final ReferenceType DEFAULT_REFERENCE_TYPE = ReferenceType.SOFT;
private static final int MAXIMUM_CONCURRENCY_LEVEL = 1 << 16;
private static final int MAXIMUM_SEGMENT_SIZE = 1 << 30;
private final Segment[] segments; 使用Segment作為hash分段桶,桶内的 寫操作 使用lock
構造參數有4個,int initialCapacity, float loadFactor, int concurrencyLevel, ReferenceType referenceType 預設值分别對應以上常量
#protected final float getLoadFactor() return this.loadFactor;
#protected final int getSegmentsSize() return this.segments.length;
#protected final Segment getSegment(int index) return this.segments[index];
#protected ReferenceManager createReferenceManager() return new ReferenceManager();
#protected int getHash(@Nullable Object o) 計算key的hash
@Nullable
#public V get(@Nullable Object key) 委托getReference(@Nullable Object key, Restructure. Restructure restructure) Restructure.WHEN_NECESSARY
#public V getOrDefault(@Nullable Object key, @Nullable V defaultValue) 若不存在則傳回defaultValue,Restructure.WHEN_NECESSARY
#public boolean containsKey(@Nullable Object key) 跟get(@Nullable Object key) 一樣先找,找到後再用key的eq,Restructure.WHEN_NECESSARY
#protected final Reference<K, V> getReference(@Nullable Object key, Restructure restructure) 使用key的hash找到Segment,在Segment中找到Reference(Restructure.WHEN_NECESSARY在找之前會先檢查ReferenceQueue中是否可以poll到,若有則restructure這個Segment,restructure是使用lock的)
#public V put(@Nullable K key, @Nullable V value) return put(key, value, true);
#public V putIfAbsent(@Nullable K key, @Nullable V value) return put(key, value, false);
#private V put(@Nullable final K key, @Nullable final V value, final boolean overwriteExisting) 使用key的hash找到Segment,執行Segment.doTask(final int hash, @Nullable final Object key, final Task<T> task) ,task參數是個回調(這裡的回調邏輯是,若key對應的value已存在,則根據overwriteExisting去覆寫;若不存在則新增)
#public V remove(@Nullable Object key) task回調邏輯是,若找到可移除的Reference,則執行Reference的release,觸發java.lang.ref.Reference的enqueue和clear,讓它進ReferenceQueue
#public boolean remove(@Nullable Object key, final @Nullable Object value) task回調邏輯比remove(@Nullable Object key) 多value的eq判斷
#public boolean replace(@Nullable K key, final @Nullable V oldValue, final @Nullable V newValue) task回調邏輯是,若找到可移除的Reference,若oldValue是eq的,則使用newValue進行覆寫
#public V replace(@Nullable K key, final @Nullable V value) task回調邏輯是,若找到可移除的Reference,則進行覆寫
#public void clear() 執行所有segment的segment.clear();
#public void purgeUnreferencedEntries() 執行所有segment的segment.restructureIfNecessary(false); 重新整理
#public int size() 所有segment的數量累加
#public boolean isEmpty() 是否所有segment都是空的
#public Set<Map.Entry<K, V>> entrySet() 産生一個内部類EntrySet的映射,資料引用ConcurrentReferenceHashMap,不會copy一份新的
#private <T> T doTask(@Nullable Object key, Task<T> task) 使用key的hash找到Segment,執行Segment.doTask(final int hash, @Nullable final Object key, final Task<T> task)
#private Segment getSegmentForHash(int hash) 找Segment的hash算法
以下是内部類
Segment
#public Reference<K, V> getReference(@Nullable Object key, int hash, Restructure restructure) Segment用于找Reference的邏輯封裝在這裡
#public <T> T doTask(final int hash, @Nullable final Object key, final Task<T> task) Segment.doTask會先檢查是否需要restructure,然後根據hash、key找到Reference,無論Reference是否找到,都執行task.execute(ref, entry, entries); 這裡的entries參數是個匿名内部類,實作Entries.add,這裡實作的邏輯是使用key、value建立Entry,建立Reference并加入到Segment
#public void clear() 重新建立一份private volatile Reference<K, V>[] references;
Reference
接口,定義了新方法
Entry<K, V> get();
int getHash();
@Nullable
Reference<K, V> getNext();
void release();
有2個實作類除了分别繼承SoftReference、WeakReference,其他一樣
SoftEntryReference<K, V> extends SoftReference<Entry<K, V>> implements Reference<K, V>
WeakEntryReference<K, V> extends WeakReference<Entry<K, V>> implements Reference<K, V>
Entry
封裝了K,V
Task
抽象類,2個execute方法
EntrySet
資料映射,資料不獨立
EntryIterator
疊代器,資料獨立
ReferenceManager