private static final int DEFAULT_INITIAL_CAPACITY = 16;
private static final int DEFAULT_CONCURRENCY_LEVEL = 4;
private static final int DEFAULT_EXPIRATION_NANOS = 0;
private static final int DEFAULT_REFRESH_NANOS = 0;
int initialCapacity = UNSET_INT; //设置初始化大小,默认16
int concurrencyLevel = UNSET_INT; //设置并发级别,默认4
long maximumSize = UNSET_INT; //缓存最大大小
long maximumWeight = UNSET_INT; //缓存最大权重
Weigher super K, ? super V> weigher; //缓存权重计算器
Strength keyStrength; //key引用强度,强引用 or 弱引用
Strength valueStrength; //value引用强度
long expireAfterWriteNanos = UNSET_INT; //写过期时间
long expireAfterAccessNanos = UNSET_INT; //读过期时间
long refreshNanos = UNSET_INT; //刷新周期
Equivalence
1.2 缓存构造
最后通过CacheBuilder的build方法构造缓存,这里分为两种情况:
不包含加载器CacheLoader,构造缓存类:LocalManualCache
包含加载器CacheLoader,构造缓存类:LocalLoadingCache
代码如下:
/**
* Builds a cache, which either returns an already-loaded value for a given key or atomically
* computes or retrieves it using the supplied {@code CacheLoader}. If another thread is currently
* loading the value for this key, simply waits for that thread to finish and returns its loaded
* value. Note that multiple threads can concurrently load values for distinct keys.
*
*
This method does not alter the state of this {@code CacheBuilder} instance, so it can be
* invoked again to create multiple independent caches.
*
* @param loader the cache loader used to obtain new values
* @return a cache having the requested features
*/
public LoadingCache build(
CacheLoader super K1, V1> loader) {
checkWeightWithWeigher();
return new LocalCache.LocalLoadingCache(this, loader);
}
/**
* Builds a cache which does not automatically load values when keys are requested.
*
*
Consider {@link #build(CacheLoader)} instead, if it is feasible to implement a
* {@code CacheLoader}.
*
*
This method does not alter the state of this {@code CacheBuilder} instance, so it can be
* invoked again to create multiple independent caches.
*
* @return a cache having the requested features
* @since 11.0
*/
public Cache build() {
checkWeightWithWeigher();
checkNonLoadingCache();
return new LocalCache.LocalManualCache(this);
}
下面看一下这两个缓存类的定义:
1.2.1 LocalManualCache类
static class LocalManualCache implements Cache, Serializable {
final LocalCache localCache;
LocalManualCache(CacheBuilder super K, ? super V> builder) {
this(new LocalCache(builder, null));
}
private LocalManualCache(LocalCache localCache) {
this.localCache = localCache;
}
// Cache methods
@Override
@Nullable
public V getIfPresent(Object key) {
return localCache.getIfPresent(key);
}
@Override
public V get(K key, final Callable extends V> valueLoader) throws ExecutionException {
checkNotNull(valueLoader);
return localCache.get(
key,
new CacheLoader() {
@Override
public V load(Object key) throws Exception {
return valueLoader.call();
}
});
}
@Override
public ImmutableMap getAllPresent(Iterable> keys) {
return localCache.getAllPresent(keys);
}
@Override
public void put(K key, V value) {
localCache.put(key, value);
}
@Override
public void putAll(Map extends K, ? extends V> m) {
localCache.putAll(m);
}
@Override
public void invalidate(Object key) {
checkNotNull(key);
localCache.remove(key);
}
@Override
public void invalidateAll(Iterable> keys) {
localCache.invalidateAll(keys);
}
@Override
public void invalidateAll() {
localCache.clear();
}
@Override
public long size() {
return localCache.longSize();
}
@Override
public ConcurrentMap asMap() {
return localCache;
}
@Override
public CacheStats stats() {
SimpleStatsCounter aggregator = new SimpleStatsCounter();
aggregator.incrementBy(localCache.globalStatsCounter);
for (Segment segment : localCache.segments) {
aggregator.incrementBy(segment.statsCounter);
}
return aggregator.snapshot();
}
@Override
public void cleanUp() {
localCache.cleanUp();
}
// Serialization Support
private static final long serialVersionUID = 1;
Object writeReplace() {
return new ManualSerializationProxy(localCache);
}
}
static class Segment extends ReentrantLock {
@Weak final LocalCache map;
/**
* The number of live elements in this segment's region.
*/
volatile int count;
/**
* The weight of the live elements in this segment's region.
*/
@GuardedBy("this")
long totalWeight;
/**
* Number of updates that alter the size of the table. This is used during bulk-read methods to
* make sure they see a consistent snapshot: If modCounts change during a traversal of segments
* loading size or checking containsValue, then we might have an inconsistent view of state so
* (usually) must retry.
*/
int modCount;
/**
* The table is expanded when its size exceeds this threshold. (The value of this field is
* always {@code (int) (capacity * 0.75)}.)
*/
int threshold;
/**
* The per-segment table.
*/
volatile AtomicReferenceArray> table;
/**
* The maximum weight of this segment. UNSET_INT if there is no maximum.
*/
final long maxSegmentWeight;
/**
* The key reference queue contains entries whose keys have been garbage collected, and which
* need to be cleaned up internally.
*/
final ReferenceQueue keyReferenceQueue;
/**
* The value reference queue contains value references whose values have been garbage collected,
* and which need to be cleaned up internally.
*/
final ReferenceQueue valueReferenceQueue;
/**
* The recency queue is used to record which entries were accessed for updating the access
* list's ordering. It is drained as a batch operation when either the DRAIN_THRESHOLD is
* crossed or a write occurs on the segment.
*/
final Queue> recencyQueue;
/**
* A counter of the number of reads since the last write, used to drain queues on a small
* fraction of read operations.
*/
final AtomicInteger readCount = new AtomicInteger();
/**
* A queue of elements currently in the map, ordered by write time. Elements are added to the
* tail of the queue on write.
*/
@GuardedBy("this")
final Queue> writeQueue;
/**
* A queue of elements currently in the map, ordered by access time. Elements are added to the
* tail of the queue on access (note that writes count as accesses).
*/
@GuardedBy("this")
final Queue> accessQueue;
/** Accumulates cache statistics. */
final StatsCounter statsCounter;
Segment(
LocalCache map,
int initialCapacity,
long maxSegmentWeight,
StatsCounter statsCounter) {
this.map = map;
this.maxSegmentWeight = maxSegmentWeight;
this.statsCounter = checkNotNull(statsCounter);
initTable(newEntryArray(initialCapacity));
keyReferenceQueue = map.usesKeyReferences() ? new ReferenceQueue() : null;
valueReferenceQueue = map.usesValueReferences() ? new ReferenceQueue() : null;
recencyQueue =
map.usesAccessQueue()
? new ConcurrentLinkedQueue>()
: LocalCache.>discardingQueue();
writeQueue =
map.usesWriteQueue()
? new WriteQueue()
: LocalCache.>discardingQueue();
accessQueue =
map.usesAccessQueue()
? new AccessQueue()
: LocalCache.>discardingQueue();
}
AtomicReferenceArray> newEntryArray(int size) {
return new AtomicReferenceArray>(size);
}
}
@Override
public V put(K key, V value) {
checkNotNull(key);
checkNotNull(value);
int hash = hash(key);
return segmentFor(hash).put(key, hash, value, false);
}
@Override
public V putIfAbsent(K key, V value) {
checkNotNull(key);
checkNotNull(value);
int hash = hash(key);
return segmentFor(hash).put(key, hash, value, true);
}
put和putIfAbsent的流程类似,这里主要分析下put的流程:
通过hash定位该key应该位于哪个Segment
调用Segment的put方法完成键值的插入
下面会解析Segment的put流程;
2.2.2 get流程
@Override
@Nullable
public V get(@Nullable Object key) {
if (key == null) {
return null;
}
int hash = hash(key);
return segmentFor(hash).get(key, hash);
}
@Nullable
public V getIfPresent(Object key) {
int hash = hash(checkNotNull(key));
V value = segmentFor(hash).get(key, hash);
if (value == null) {
globalStatsCounter.recordMisses(1);
} else {
globalStatsCounter.recordHits(1);
}
return value;
}
@Nullable
public V getOrDefault(@Nullable Object key, @Nullable V defaultValue) {
V result = get(key);
return (result != null) ? result : defaultValue;
}
通过hash定位该key应该位于哪个Segment
调用Segment的get方法进行查询
下面会解析Segment的get流程;
2.2.3 getOrLoad流程
V get(K key, CacheLoader super K, V> loader) throws ExecutionException {
int hash = hash(checkNotNull(key));
return segmentFor(hash).get(key, hash, loader);
}
V getOrLoad(K key) throws ExecutionException {
return get(key, defaultLoader);
}
@Nullable
V put(K key, int hash, V value, boolean onlyIfAbsent) {
lock(); //每个segment写操作都需要加锁
try {
long now = map.ticker.read();
preWriteCleanup(now); //put前的一些操作,1.垃圾回收的键值进行移除 2.过期的键值进行移除
int newCount = this.count + 1;
if (newCount > this.threshold) { // ensure capacity
expand();
newCount = this.count + 1;
}
AtomicReferenceArray> table = this.table;
int index = hash & (table.length() - 1);
ReferenceEntry first = table.get(index);
// Look for an existing entry.
for (ReferenceEntry e = first; e != null; e = e.getNext()) {
K entryKey = e.getKey();
if (e.getHash() == hash
&& entryKey != null
&& map.keyEquivalence.equivalent(key, entryKey)) {
// We found an existing entry.
ValueReference valueReference = e.getValueReference();
V entryValue = valueReference.get();
if (entryValue == null) {
++modCount;
if (valueReference.isActive()) {
enqueueNotification(
key, hash, entryValue, valueReference.getWeight(), RemovalCause.COLLECTED);
setValue(e, key, value, now);
newCount = this.count; // count remains unchanged
} else {
setValue(e, key, value, now);
newCount = this.count + 1;
}
this.count = newCount; // write-volatile
evictEntries(e);
return null;
} else if (onlyIfAbsent) {
// Mimic
// "if (!map.containsKey(key)) ...
// else return map.get(key);
recordLockedRead(e, now);
return entryValue;
} else {
// clobber existing entry, count remains unchanged
++modCount;
enqueueNotification(
key, hash, entryValue, valueReference.getWeight(), RemovalCause.REPLACED);
setValue(e, key, value, now);
evictEntries(e);
return entryValue;
}
}
}
// Create a new entry.
++modCount;
ReferenceEntry newEntry = newEntry(key, hash, first);
setValue(newEntry, key, value, now);
table.set(index, newEntry);
newCount = this.count + 1;
this.count = newCount; // write-volatile
evictEntries(newEntry);
return null;
} finally {
unlock();
postWriteCleanup();
}
}
put方法主要包含流程有:
put前需要完成:1)垃圾回收的键值进行移除 2)过期的键值进行移除
元素个数达到阈值threshold,调用expand方法进行扩容
查找table是否已存在,存在更新(onlyIfAbsent=true),不存在插入
调用evictEntries方法查看是否需要移除元素,移除accessTime最早的元素;
通知removalListener;
3.2 get流程
@Nullable
V get(Object key, int hash) {
try {
if (count != 0) { // read-volatile
long now = map.ticker.read();
ReferenceEntry e = getLiveEntry(key, hash, now);
if (e == null) {
return null;
}
V value = e.getValueReference().get();
if (value != null) {
recordRead(e, now);
return scheduleRefresh(e, e.getKey(), hash, value, now, map.defaultLoader);
}
tryDrainReferenceQueues();
}
return null;
} finally {
postReadCleanup();
}
}
@Nullable
ReferenceEntry getLiveEntry(Object key, int hash, long now) {
ReferenceEntry e = getEntry(key, hash);
if (e == null) {
return null;
} else if (map.isExpired(e, now)) {
tryExpireEntries(now);
return null;
}
return e;
}
查询元素是否存在,不存在返回null
校验该元素是否过期,如果过期,清理所有过期元素,然后返回null;
校验value值是否为null(垃圾回收),如果为null,批次清理垃圾回收的元素,返回null
value不为null,重新设置accessTime为now
是否配置了刷新周期,达到刷新周期,调用刷新函数进行刷新,refresh流程阐述如下;
3.3 refresh流程
/**
* Refreshes the value associated with {@code key}, unless another thread is already doing so.
* Returns the newly refreshed value associated with {@code key} if it was refreshed inline, or
* {@code null} if another thread is performing the refresh or if an error occurs during
* refresh.
*/
@Nullable
V refresh(K key, int hash, CacheLoader super K, V> loader, boolean checkTime) {
final LoadingValueReference loadingValueReference =
insertLoadingValueReference(key, hash, checkTime);
if (loadingValueReference == null) {
return null;
}
ListenableFuture result = loadAsync(key, hash, loadingValueReference, loader);
if (result.isDone()) {
try {
return Uninterruptibles.getUninterruptibly(result);
} catch (Throwable t) {
// don't let refresh exceptions propagate; error was already logged
}
}
return null;
}
ListenableFuture loadAsync(
final K key,
final int hash,
final LoadingValueReference loadingValueReference,
CacheLoader super K, V> loader) {
final ListenableFuture loadingFuture = loadingValueReference.loadFuture(key, loader);
loadingFuture.addListener(
new Runnable() {
@Override
public void run() {
try {
getAndRecordStats(key, hash, loadingValueReference, loadingFuture);
} catch (Throwable t) {
logger.log(Level.WARNING, "Exception thrown during refresh", t);
loadingValueReference.setException(t);
}
}
},
directExecutor());
return loadingFuture;
}
/**
* An object that can receive a notification when an entry is removed from a cache. The removal
* resulting in notification could have occured to an entry being manually removed or replaced, or
* due to eviction resulting from timed expiration, exceeding a maximum size, or garbage collection.
*
*
An instance may be called concurrently by multiple threads to process different entries.
* Implementations of this interface should avoid performing blocking calls or synchronizing on
* shared resources.
*
* @param the most general type of keys this listener can listen for; for example {@code Object}
* if any key is acceptable
* @param the most general type of values this listener can listen for; for example
* {@code Object} if any key is acceptable
* @author Charles Fry
* @since 10.0
*/
@GwtCompatible
public interface RemovalListener {
/**
* Notifies the listener that a removal occurred at some point in the past.
*
*
This does not always signify that the key is now absent from the cache, as it may have
* already been re-added.
*/
// Technically should accept RemovalNotification extends K, ? extends V>, but because
// RemovalNotification is guaranteed covariant, let's make users' lives simpler.
void onRemoval(RemovalNotification notification);
}
/**
* A notification of the removal of a single entry. The key and/or value may be null if they were
* already garbage collected.
*
*
Like other {@code Map.Entry} instances associated with {@code CacheBuilder}, this class holds
* strong references to the key and value, regardless of the type of references the cache may be
* using.
*
* @author Charles Fry
* @since 10.0
*/
@GwtCompatible
public final class RemovalNotification extends SimpleImmutableEntry {
private final RemovalCause cause;
/**
* Creates a new {@code RemovalNotification} for the given {@code key}/{@code value} pair, with
* the given {@code cause} for the removal. The {@code key} and/or {@code value} may be
* {@code null} if they were already garbage collected.
*
* @since 19.0
*/
public static RemovalNotification create(
@Nullable K key, @Nullable V value, RemovalCause cause) {
return new RemovalNotification(key, value, cause);
}
private RemovalNotification(@Nullable K key, @Nullable V value, RemovalCause cause) {
super(key, value);
this.cause = checkNotNull(cause);
}
/**
* Returns the cause for which the entry was removed.
*/
public RemovalCause getCause() {
return cause;
}
/**
* Returns {@code true} if there was an automatic removal due to eviction (the cause is neither
* {@link RemovalCause#EXPLICIT} nor {@link RemovalCause#REPLACED}).
*/
public boolean wasEvicted() {
return cause.wasEvicted();
}
private static final long serialVersionUID = 0;
}
父类SimpleImmutableEntry是一个不可变对象,定义如下:
/**
* An Entry maintaining an immutable key and value. This class
* does not support method setValue. This class may be
* convenient in methods that return thread-safe snapshots of
* key-value mappings.
*
* @since 1.6
*/
public static class SimpleImmutableEntry
implements Entry, java.io.Serializable
{
private static final long serialVersionUID = 7138329143949025153L;
private final K key;
private final V value;
/**
* Creates an entry representing a mapping from the specified
* key to the specified value.
*
* @param key the key represented by this entry
* @param value the value represented by this entry
*/
public SimpleImmutableEntry(K key, V value) {
this.key = key;
this.value = value;
}
/**
* Creates an entry representing the same mapping as the
* specified entry.
*
* @param entry the entry to copy
*/
public SimpleImmutableEntry(Entry extends K, ? extends V> entry) {
this.key = entry.getKey();
this.value = entry.getValue();
}
/**
* Returns the key corresponding to this entry.
*
* @return the key corresponding to this entry
*/
public K getKey() {
return key;
}
/**
* Returns the value corresponding to this entry.
*
* @return the value corresponding to this entry
*/
public V getValue() {
return value;
}
/**
* Replaces the value corresponding to this entry with the specified
* value (optional operation). This implementation simply throws
* UnsupportedOperationException, as this class implements
* an immutable map entry.
*
* @param value new value to be stored in this entry
* @return (Does not return)
* @throws UnsupportedOperationException always
*/
public V setValue(V value) {
throw new UnsupportedOperationException();
}
/**
* Compares the specified object with this entry for equality.
* Returns {@code true} if the given object is also a map entry and
* the two entries represent the same mapping. More formally, two
* entries {@code e1} and {@code e2} represent the same mapping
* if
* This ensures that the {@code equals} method works properly across
* different implementations of the {@code Map.Entry} interface.
*
* @param o object to be compared for equality with this map entry
* @return {@code true} if the specified object is equal to this map
* entry
* @see #hashCode
*/
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry,?> e = (Map.Entry,?>)o;
return eq(key, e.getKey()) && eq(value, e.getValue());
}
/**
* Returns the hash code value for this map entry. The hash code
* of a map entry {@code e} is defined to be:
* This ensures that {@code e1.equals(e2)} implies that
* {@code e1.hashCode()==e2.hashCode()} for any two Entries
* {@code e1} and {@code e2}, as required by the general
* contract of {@link Object#hashCode}.
*
* @return the hash code value for this map entry
* @see #equals
*/
public int hashCode() {
return (key == null ? 0 : key.hashCode()) ^
(value == null ? 0 : value.hashCode());
}
/**
* Returns a String representation of this map entry. This
* implementation returns the string representation of this
* entry's key followed by the equals character ("=")
* followed by the string representation of this entry's value.
*
* @return a String representation of this map entry
*/
public String toString() {
return key + "=" + value;
}
}
/**
* The reason why a cached entry was removed.
*
* @author Charles Fry
* @since 10.0
*/
@GwtCompatible
public enum RemovalCause {
/**
* The entry was manually removed by the user. This can result from the user invoking
* {@link Cache#invalidate}, {@link Cache#invalidateAll(Iterable)}, {@link Cache#invalidateAll()},
* {@link Map#remove}, {@link ConcurrentMap#remove}, or {@link Iterator#remove}.
*/
EXPLICIT {
@Override
boolean wasEvicted() {
return false;
}
},
/**
* The entry itself was not actually removed, but its value was replaced by the user. This can
* result from the user invoking {@link Cache#put}, {@link LoadingCache#refresh}, {@link Map#put},
* {@link Map#putAll}, {@link ConcurrentMap#replace(Object, Object)}, or
* {@link ConcurrentMap#replace(Object, Object, Object)}.
*/
REPLACED {
@Override
boolean wasEvicted() {
return false;
}
},
/**
* The entry was removed automatically because its key or value was garbage-collected. This can
* occur when using {@link CacheBuilder#weakKeys}, {@link CacheBuilder#weakValues}, or
* {@link CacheBuilder#softValues}.
*/
COLLECTED {
@Override
boolean wasEvicted() {
return true;
}
},
/**
* The entry's expiration timestamp has passed. This can occur when using
* {@link CacheBuilder#expireAfterWrite} or {@link CacheBuilder#expireAfterAccess}.
*/
EXPIRED {
@Override
boolean wasEvicted() {
return true;
}
},
/**
* The entry was evicted due to size constraints. This can occur when using
* {@link CacheBuilder#maximumSize} or {@link CacheBuilder#maximumWeight}.
*/
SIZE {
@Override
boolean wasEvicted() {
return true;
}
};
/**
* Returns {@code true} if there was an automatic removal due to eviction (the cause is neither
* {@link #EXPLICIT} nor {@link #REPLACED}).
*/
abstract boolean wasEvicted();
}
/**
* A collection of common removal listeners.
*
* @author Charles Fry
* @since 10.0
*/
@GwtIncompatible
public final class RemovalListeners {
private RemovalListeners() {}
/**
* Returns a {@code RemovalListener} which processes all eviction notifications using
* {@code executor}.
*
* @param listener the backing listener
* @param executor the executor with which removal notifications are asynchronously executed
*/
public static RemovalListener asynchronous(
final RemovalListener listener, final Executor executor) {
checkNotNull(listener);
checkNotNull(executor);
return new RemovalListener() {
@Override
public void onRemoval(final RemovalNotification notification) {
executor.execute(
new Runnable() {
@Override
public void run() {
listener.onRemoval(notification);
}
});
}
};
}
}
package spark.examples.scala.grammars.caseclasses
object CaseClass_Test00 {
def simpleMatch(arg: Any) = arg match {
case v: Int => "This is an Int"
case v: (Int, String)