天天看點

ConcurrentReferenceHashMap

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