/**
* Copyright (c) 2016-2021, Bosco.Liao (bosco_liao@126.com).
*
* 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.iherus.shiro.util.concurrent;
import org.iherus.shiro.util.Utils;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
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;
import java.util.concurrent.locks.ReentrantLock;
/**
* Copy from {@link org.springframework.util.ConcurrentReferenceHashMap}.
* <p>
* A {@link ConcurrentHashMap} that uses {@link ReferenceType#SOFT soft} or
* {@linkplain ReferenceType#WEAK weak} references for both {@code keys} and {@code values}.
*
* <p>This class can be used as an alternative to
* {@code Collections.synchronizedMap(new WeakHashMap<K, Reference<V>>())} in order to
* support better performance when accessed concurrently. This implementation follows the
* same design constraints as {@link ConcurrentHashMap} with the exception that
* {@code null} values and {@code null} keys are supported.
*
* <p><b>NOTE:</b> The use of references means that there is no guarantee that items
* placed into the map will be subsequently available. The garbage collector may discard
* references at any time, so it may appear that an unknown thread is silently removing
* entries.
*
* <p>If not explicitly specified, this implementation will use
* {@linkplain SoftReference soft entry references}.
*
* @param <K> the key type
* @param <V> the value type
*
* @author Phillip Webb
* @author Juergen Hoeller
* @modifier Bosco.Liao
*/
public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> {
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.WEAK;
private static final int MAXIMUM_CONCURRENCY_LEVEL = 1 << 16;
private static final int MAXIMUM_SEGMENT_SIZE = 1 << 30;
/**
* Array of segments indexed using the high order bits from the hash.
*/
private final Segment[] segments;
/**
* When the average number of references per table exceeds this value resize will be attempted.
*/
private final float loadFactor;
/**
* The reference type: SOFT or WEAK.
*/
private final ReferenceType referenceType;
/**
* The shift value used to calculate the size of the segments array and an index from the hash.
*/
private final int shift;
/**
* Late binding entry set.
*/
private volatile Set<Map.Entry<K, V>> entrySet;
/**
* Create a new {@code ConcurrentReferenceHashMap} instance.
*/
public ConcurrentReferenceHashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL, DEFAULT_REFERENCE_TYPE);
}
/**
* Create a new {@code ConcurrentReferenceHashMap} instance.
*
* @param initialCapacity the initial capacity of the map
*/
public ConcurrentReferenceHashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL, DEFAULT_REFERENCE_TYPE);
}
/**
* Create a new {@code ConcurrentReferenceHashMap} instance.
*
* @param initialCapacity the initial capacity of the map
* @param loadFactor the load factor. When the average number of references per table
* exceeds this value resize will be attempted
*/
public ConcurrentReferenceHashMap(int initialCapacity, float loadFactor) {
this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL, DEFAULT_REFERENCE_TYPE);
}
/**
* Create a new {@code ConcurrentReferenceHashMap} instance.
*
* @param initialCapacity the initial capacity of the map
* @param concurrencyLevel the expected number of threads that will concurrently
* write to the map
*/
public ConcurrentReferenceHashMap(int initialCapacity, int concurrencyLevel) {
this(initialCapacity, DEFAULT_LOAD_FACTOR, concurrencyLevel, DEFAULT_REFERENCE_TYPE);
}
/**
* Create a new {@code ConcurrentReferenceHashMap} instance.
*
* @param initialCapacity the initial capacity of the map
* @param referenceType the reference type used for entries (soft or weak)
*/
public ConcurrentReferenceHashMap(int initialCapacity, ReferenceType referenceType) {
this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL, referenceType);
}
/**
* Create a new {@code ConcurrentReferenceHashMap} instance.
*
* @param initialCapacity the initial capacity of the map
* @param loadFactor the load factor. When the average number of references per
* table exceeds this value, resize will be attempted.
* @param concurrencyLevel the expected number of threads that will concurrently
* write to the map
*/
public ConcurrentReferenceHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
this(initialCapacity, loadFactor, concurrencyLevel, DEFAULT_REFERENCE_TYPE);
}
/**
* Create a new {@code ConcurrentReferenceHashMap} instance.
*
* @param initialCapacity the initial capacity of the map
* @param loadFactor the load factor. When the average number of references per
* table exceeds this value, resize will be attempted.
* @param concurrencyLevel the expected number of threads that will concurrently
* write to the map
* @param referenceType the reference type used for entries (soft or weak)
*/
@SuppressWarnings("unchecked")
public ConcurrentReferenceHashMap(
int initialCapacity, float loadFactor, int concurrencyLevel, ReferenceType referenceType) {
isTrue(initialCapacity >= 0, "Initial capacity must not be negative");
isTrue(loadFactor > 0f, "Load factor must be positive");
isTrue(concurrencyLevel > 0, "Concurrency level must be positive");
notNull(referenceType, "Reference type must not be null");
this.loadFactor = loadFactor;
this.shift = calculateShift(concurrencyLevel, MAXIMUM_CONCURRENCY_LEVEL);
int size = 1 << this.shift;
this.referenceType = referenceType;
int roundedUpSegmentCapacity = (int) ((initialCapacity + size - 1L) / size);
this.segments = (Segment[]) Array.newInstance(Segment.class, size);
for (int i = 0; i < this.segments.length; i++) {
this.segments[i] = new Segment(roundedUpSegmentCapacity);
}
}
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];
}
/**
* Factory method that returns the {@link ReferenceManager}.
* Thi