Guava缓存器源码分析——键值类型
昨天分析了Guava缓存器的数据存取过程,下面介绍下键值类型所涉及的类图: Guava缓存器中键值都通过工厂模式来创建,工厂类型为枚举变量,包括以下8种:STRONG, STRONG_ACCESS, STRONG_WRITE, STRONG_ACCESS_WRITE, WEAK, WEAK_ACCESS, WEAK_WRITE, WEAK_ACCESS_WRITE ,存放在EntryFactory数组中。 构造缓存器时,初始化键值工厂: entryFactory = EntryFactory.getFactory(keyStrength, usesAccessEntries(), usesWriteEntries()); 其中,keyStrength为键的引用级别, 将usesAccessEntries()可展开为 expireAfterAccessNanos > 0 || maxWeight >= 0,表示设置了访问过期时间或权重参数,usesWriteEntries()可展开为expireAfterWriteNanos > 0 || refreshNanos > 0,表示设置了写过期时间或刷新时间。 键值工厂的获取: static EntryFactory getFactory(Strength keyStrength, boolean usesAccessQueue, boolean usesWriteQueue) { int flags = ((keyStrength == Strength.WEAK) ? WEAK_MASK : 0) | (usesAccessQueue ? ACCESS_MASK : 0) | (usesWriteQueue ? WRITE_MASK : 0); return factories[flags]; } 每种工厂都有各自的newEntry方法,默认引用级别下,newEntry方法产生的键值引用为StrongEntry类型。 STRONG { @Override <K, V> ReferenceEntry<K, V> newEntry( Segment<K, V> segment, K key, int hash, @Nullable ReferenceEntry<K, V> next) { return new StrongEntry<K, V>(key, hash, next); } } 强引用级别的缓存数据,将直接存储缓存键的引用,其它级别的缓存数据,将缓存键的引用委托给它们的父类。
在查询数据时,如果需要加载数据(不存在或过期),首先会创建LoadingValueReference类型的值引用,若数据不存在,则需要创建并初始化新的键值引用(newEntry()),若数据过期只需要重新设置值引用即可:setValueReference(loadingValueReference); 实际的数据加载是通过调用LoadingValueReference类的loadFuture方法,该方法会调用缓存器构建时用户自定义的数据加载方法load() || reload(),并通过Stopwatch记录数据加载时间。
Guava缓存器中涉及到键值的底层操作都通过Unsafe类来实现,Unsafe类提供了硬件级别的原子操作,Java无法直接访问到操作系统底层(如系统硬件等),为此Java使用native方法来扩展Java程序的功能。 例如: public final boolean compareAndSet(int i, E expect, E update) { return unsafe.compareAndSwapObject(array, rawIndex(i), expect, update); } 该方法在array的 rawIndex(i)位置比较object field和期望的值expect,如果相同则更新为update。 这里顺便介绍下Compare And Swap(CAS)操作:CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。