这篇文章主要介绍JDK如何实现WeakCache缓存,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!
朔城网站建设公司创新互联,朔城网站设计制作,有大型网站制作公司丰富经验。已为朔城近1000家提供企业网站建设服务。企业网站搭建\外贸营销网站建设要多少钱,请找那个售后服务好的朔城做网站的公司定做!
示例:
//Reference引用队列 private final ReferenceQueuerefQueue = new ReferenceQueue<>(); //缓存的底层实现, key为一级缓存, value为二级缓存。 为了支持null, map的key类型设置为Object private final ConcurrentMap
首先我们看一下WeakCache的成员变量和构造器,WeakCache缓存的内部实现是通过ConcurrentMap来完成的,成员变量map就是二级缓存的底层实现,reverseMap是为了实现缓存的过期机制,subKeyFactory是二级缓存key的生成工厂,通过构造器传入,这里传入的值是Proxy类的KeyFactory,valueFactory是二级缓存value的生成工厂,通过构造器传入,这里传入的是Proxy类的ProxyClassFactory。接下来我们看一下WeakCache的get方法。
public V get(K key, P parameter) { //这里要求实现的接口不能为空 Objects.requireNonNull(parameter); //清除过期的缓存 expungeStaleEntries(); //将ClassLoader包装成CacheKey, 作为一级缓存的key Object cacheKey = CacheKey.valueOf(key, refQueue); //获取得到二级缓存 ConcurrentMap
WeakCache的get方法并没有用锁进行同步,那它是怎样实现线程安全的呢?因为它的所有会进行修改的成员变量都使用了ConcurrentMap,这个类是线程安全的。因此它将自身的线程安全委托给了ConcurrentMap, get方法尽可能的将同步代码块缩小,这样可以有效提高WeakCache的性能。我们看到ClassLoader作为了一级缓存的key,这样可以首先根据ClassLoader筛选一遍,因为不同ClassLoader加载的类是不同的。然后它用接口数组来生成二级缓存的key,这里它进行了一些优化,因为大部分类都是实现了一个或两个接口,所以二级缓存key分为key0,key1,key2,keyX。key0到key2分别表示实现了0到2个接口,keyX表示实现了3个或以上的接口,事实上大部分都只会用到key1和key2。这些key的生成工厂是在Proxy类中,通过WeakCache的构造器将key工厂传入。这里的二级缓存的值是一个Factory实例,最终代理类的值是通过Factory这个工厂来获得的。
private final class Factory implements Supplier{ //一级缓存key, 根据ClassLoader生成 private final K key; //代理类实现的接口数组 private final P parameter; //二级缓存key, 根据接口数组生成 private final Object subKey; //二级缓存 private final ConcurrentMap > valuesMap; Factory(K key, P parameter, Object subKey, ConcurrentMap > valuesMap) { this.key = key; this.parameter = parameter; this.subKey = subKey; this.valuesMap = valuesMap; } @Override public synchronized V get() { //这里再一次去二级缓存里面获取Supplier, 用来验证是否是Factory本身 Supplier supplier = valuesMap.get(subKey); if (supplier != this) { //在这里验证supplier是否是Factory实例本身, 如果不则返回null让调用者继续轮询重试 //期间supplier可能替换成了CacheValue, 或者由于生成代理类失败被从二级缓存中移除了 return null; } V value = null; try { //委托valueFactory去生成代理类, 这里会通过传入的ProxyClassFactory去生成代理类 value = Objects.requireNonNull(valueFactory.apply(key, parameter)); } finally { //如果生成代理类失败, 就将这个二级缓存删除 if (value == null) { valuesMap.remove(subKey, this); } } //只有value的值不为空才能到达这里 assert value != null; //使用弱引用包装生成的代理类 CacheValue cacheValue = new CacheValue<>(value); //将包装后的cacheValue放入二级缓存中, 这个操作必须成功, 否则就报错 if (valuesMap.replace(subKey, this, cacheValue)) { //将cacheValue成功放入二级缓存后, 再对它进行标记 reverseMap.put(cacheValue, Boolean.TRUE); } else { throw new AssertionError("Should not reach here"); } //最后返回没有被弱引用包装的代理类 return value; } }
我们再看看Factory这个内部工厂类,可以看到它的get方法是使用synchronized关键字进行了同步。进行get方法后首先会去验证subKey对应的suppiler是否是工厂本身,如果不是就返回null,而WeakCache的get方法会继续进行重试。如果确实是工厂本身,那么就会委托ProxyClassFactory生成代理类,ProxyClassFactory是在构造WeakCache的时候传入的。所以这里解释了为什么最后会调用到Proxy的ProxyClassFactory这个内部工厂来生成代理类。生成代理类后使用弱引用进行包装并放入reverseMap中,最后会返回原装的代理类。
以上是“JDK如何实现WeakCache缓存”这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注创新互联行业资讯频道!