guava的LoadingCache原理

Guava Cache 原理分析与最佳实践 本讲主要针对本地 Cache 的老大哥 Guava Cache 进行介绍和分析。 阅读详情

用法

用户需要覆com.google.common.cache.CacheLoader#reload这个方法。这个方法返回一个 ListenableFuture。这个对象可以用

  public static <V> ListenableFutureTask<V> create(Callable<V> callable) {
    return new ListenableFutureTask<V>(callable);
  }

从一个Callable对象进行转换。
在这里插入图片描述
guava推荐你使用异步的方式来进行刷新。

同时需要覆盖com.google.common.cache.CacheLoader#load这个方法。

例子

        Cache<String, Long> xxx =  CacheBuilder.newBuilder()
                .expireAfterWrite(5, TimeUnit.MINUTES)
                .initialCapacity(20)
                .build();

原理

一般使用的是LocalLoadingCache。继承LocalManualCache。
在这里插入图片描述

    /**
     * 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<ReferenceEntry<K, V>> recencyQueue;


    /**
     * A queue of elements currently in the map, ordered by write time. Elements are added to the
     * tail of the queue on write.
     */
    //最近写队列,按写入照时间排序。注意所有现在map里有的元素都在这里。write时进行写入
    @GuardedBy("this")
    final Queue<ReferenceEntry<K, V>> 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).
     */

	//最近访问队列,按照访问时间排序。实际上是write的数量加上访问load数据的数量。注意所有现在map里有的元素都在这里。write时同样会写入。
    @GuardedBy("this")
    final Queue<ReferenceEntry<K, V>> accessQueue;

	//实际存储的entry数组
    /**
     * The per-segment table.
     */
    volatile AtomicReferenceArray<ReferenceEntry<K, V>> table;

存储的实际对象是ReferenceEntry

    /**
     * Returns the time that this entry was last accessed, in ns.
     */
    long getAccessTime();

    /**
     * Sets the entry access time in ns.
     */
    void setAccessTime(long time);
        /**
     * Returns the time that this entry was last written, in ns.
     */
    long getWriteTime();

    /**
     * Sets the entry write time in ns.
     */
    void setWriteTime(long time);


    /**
     * Returns the next entry in the write queue.
     */
    ReferenceEntry<K, V> getNextInWriteQueue();

    /**
     * Sets the next entry in the write queue.
     */
    void setNextInWriteQueue(ReferenceEntry<K, V> next);

    /**
     * Returns the previous entry in the write queue.
     */
    ReferenceEntry<K, V> getPreviousInWriteQueue();

    /**
     * Sets the previous entry in the write queue.
     */
    void setPreviousInWriteQueue(ReferenceEntry<K, V> previous);

    /**
     * Returns the next entry in the access queue.
     */
    ReferenceEntry<K, V> getNextInAccessQueue();

    /**
     * Sets the next entry in the access queue.
     */
    void setNextInAccessQueue(ReferenceEntry<K, V> next);

    /**
     * Returns the previous entry in the access queue.
     */
    ReferenceEntry<K, V> getPreviousInAccessQueue();

    /**
     * Sets the previous entry in the access queue.
     */
    void setPreviousInAccessQueue(ReferenceEntry<K, V> previous);

值得注意的这个类持有AccessQueue和WriteQueue的双向指针。这让它可以在o(1)的时间判断自己有没有在这两个队列里,并在o(1)的时间把自己删除。

getIfPresent

其实和concurrentMap实现的原理是完全一致的

  @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;
  }

GET方法


    @Override
    public V get(K key) throws ExecutionException {
      return localCache.getOrLoad(key);
    }

  @Override
  @Nullable
  V get(K key, CacheLoader<? super K, V> loader) throws ExecutionException {
    int hash = hash(checkNotNull(key));
    return segmentFor(hash).get(key, hash, loader);
  }

直接调用到相关的segment中

    V get(K key, int hash, CacheLoader<? super K, V> loader) throws ExecutionException {
      checkNotNull(key);
      checkNotNull(loader);
      try {
        if (count != 0) { // read-volatile
          // don't call getLiveEntry, which would ignore loading values
          ReferenceEntry<K, V> e = getEntry(key, hash);//获取实际的值
          if (e != null) {
            long now = map.ticker.read();
            V value = getLiveValue(e, now);//尝试在缓存中获取
            if (value != null) {
              recordRead(e, now);//记录读取,把entry放进recencyQueue
              statsCounter.recordHits(1);
              return scheduleRefresh(e, key, hash, value, now, loader);
            }
            ValueReference<K, V> valueReference = e.getValueReference();
            if (valueReference.isLoading()) {
              return waitForLoadingValue(e, key, valueReference);
            }
          }
        }

        // at this point e is either null or expired;
        return lockedGetOrLoad(key, hash, loader);//如果不存在则调用对用的loader进行读取
      } catch (ExecutionException ee) {
        Throwable cause = ee.getCause();
        if (cause instanceof Error) {
          throw new ExecutionError((Error) cause);
        } else if (cause instanceof RuntimeException) {
          throw new UncheckedExecutionException(cause);
        }
        throw ee;
      } finally {
        //在读请求一定次次数后会触发清理操作
        postReadCleanup();
      }
    }

  • 每次无锁的读操作都会去写而且只会写最近读队列(将entry无脑入队,见方法:recordRead)。为什么不直接写到acessQueue呢?因为如果要写acessQueue,就不可能避免要加锁。

  • 每次锁保护下写操作都会涉及到最近访问队列的读写,比如每次向缓存新增元素都会做几次清理工作,清理就需要读accessQueue(淘汰掉队头的元素,见方法expireEntries、evictEntries);每次向缓存新增元素成功后记录元素写操作,记录会写accessQueue(加到队尾,见方法recordWrite)。每次访问accessQueue前都需要先排干最近读队列至accessQueue中(按先进先出顺序,相当于批量调整accessQueue中元素顺序),然后再去进行accessQueue的读或者写操作,以尽量保证accessQueue中元素顺序和真实的最近访问顺序一致(见方法:drainRecencyQueue)

  • 如果在写少读非常多的场景下,读写accessQueue的机会很少,大量读操作会在recencyQueue中累积很多元素占用内存而得不到排干的机会。所以Guava为了解决这个问题,为读操作设置了一个DRAIN_THRESHOLD,当累积读次数达到排干阈值时也会触发一次清理操作,从而排干recencyQueue到accessQueue。

PUT方法

    @Nullable
    V put(K key, int hash, V value, boolean onlyIfAbsent) {
      lock();
      try {
        long now = map.ticker.read();
        preWriteCleanup(now);

        int newCount = this.count + 1;
        if (newCount > this.threshold) { // ensure capacity
          expand();
          newCount = this.count + 1;
        }

        AtomicReferenceArray<ReferenceEntry<K, V>> table = this.table;
        int index = hash & (table.length() - 1);
        ReferenceEntry<K, V> first = table.get(index);

        // Look for an existing entry.
        for (ReferenceEntry<K, V> 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<K, V> valueReference = e.getValueReference();
            V entryValue = valueReference.get();

            if (entryValue == null) {
              ++modCount;
              if (valueReference.isActive()) {
                enqueueNotification(key, hash, valueReference, 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, valueReference, RemovalCause.REPLACED);
              setValue(e, key, value, now);
              evictEntries(e);
              return entryValue;
            }
          }
        }

        // Create a new entry.
        ++modCount;
        ReferenceEntry<K, V> 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();
      }
    }

中间会调用evictEntries,如果otalWeight > maxSegmentWeight,就会执行清理,舍弃缓存

      while (totalWeight > maxSegmentWeight) {
        ReferenceEntry<K, V> e = getNextEvictable();
        if (!removeEntry(e, e.getHash(), RemovalCause.SIZE)) {
          throw new AssertionError();
        }
      }

获取要舍弃的缓存,只需要从accessQueue对尾部来拿就可以了

    @GuardedBy("this")
    ReferenceEntry<K, V> getNextEvictable() {
      for (ReferenceEntry<K, V> e : accessQueue) {
        int weight = e.getValueReference().getWeight();
        if (weight > 0) {
          return e;
        }
      }
      throw new AssertionError();
    }

删除元素

在每次写操作时都会去尝试执行清理逻辑,在累积一定读操作后也会去尝试执行清理逻辑,以保证在一个少写多读的场景下过期元素也能有机会去清理(另外,在读操作时如果发现元素过期了/被垃圾回收也会触发清理逻辑)。同时LoadingCache也建议在一个写非常少、读非常多的场景下,使用者自行创建一个清理线程执行Cache.cleanUp()来清理过期元素。清理工作分为两部分:

  • 一部分是将过期元素从内部各种数据结构里删掉,涉及到对内部数据结构的变更,因此需要在段锁保护下进行;
  • 另一部分是执行用户的元素删除事件监听器逻辑,这部分执行的是用户逻辑,可能会有很重的操作,因此必须不能在锁保护下进行,执行前需要释放取到的段锁。

清理是调用segment的cleanup

    void cleanUp() {
      long now = map.ticker.read();
      runLockedCleanup(now);
      //通知listener
      runUnlockedCleanup();
    }

过期删除的实现

    @GuardedBy("this")
    void expireEntries(long now) {
      drainRecencyQueue();//将所有recencyQueue放到accessQueue(如果该元素存在于accessQueue的话)

      ReferenceEntry<K, V> e;
      //分别遍历writeQueue和accessQueue。如果已经过期了,就删除掉
      while ((e = writeQueue.peek()) != null && map.isExpired(e, now)) {
        if (!removeEntry(e, e.getHash(), RemovalCause.EXPIRED)) {
          throw new AssertionError();
        }
      }
      while ((e = accessQueue.peek()) != null && map.isExpired(e, now)) {
        if (!removeEntry(e, e.getHash(), RemovalCause.EXPIRED)) {
          throw new AssertionError();
        }
      }
    }

需要删除链表中的某个元素时,不直接在原链表上删除,而是将原链表从表头到待删除元素的前一个节点entry全部copy一份,形成一个新的链表,从而不改变原链表的结构。具体算法设计的有点巧妙,见下:

    @GuardedBy("this")
    @Nullable
    ReferenceEntry<K, V> removeEntryFromChain(ReferenceEntry<K, V> first,
        ReferenceEntry<K, V> entry) {
      int newCount = count;
      ReferenceEntry<K, V> newFirst = entry.getNext();
      for (ReferenceEntry<K, V> e = first; e != entry; e = e.getNext()) {
        ReferenceEntry<K, V> next = copyEntry(e, newFirst);
        if (next != null) {
          newFirst = next;
        } else {
          removeCollectedEntry(e);
          newCount--;
        }
      }
      this.count = newCount;
      return newFirst;
    }

在这里插入图片描述
删除3后
在这里插入图片描述

从零到一:Gromacs伞形采样的实战避坑指南 本文详细介绍了Gromacs伞形采样技术的实战应用与避坑指南,涵盖核心原理、环境准备、参数设置、数据分析及性能优化等关键环节。特别针对蛋白质-配体结合自由能计算等典型应用场景,提供了弹簧常数设置、采样窗口间距等实用参数建议,帮助研究人员高效完成分子动力学模拟。 阅读详情

相关推荐

i茅台APP加密参数逆向分析:MT-Device-ID与mt-v生成算法

在移动应用安全领域,参数加密是防止API滥用的重要手段。通过对通信协议的逆向分析,可以揭示加密参数的生成原理,这对理解移动应用安全机制具有重要意义。以i茅台APP为例,其关键接口使用了MT-Device-ID和mt-v两个加密参数,前者基于设备硬件信息生成,后者则通过动态签名机制确保请求时效性。这类技术广泛应用于电商、金融等需要高安全性的场景。通过抓包工具和逆向工程手段,可以还原出从设备指纹生成到动态签名的完整技术链条,其中涉及MD5、SHA256等常见哈希算法的应用。掌握这些加密参数的生成逻辑,不仅有助于

weixin_28568819的博客 321

guava之LoadingCache

最正统的查询 LoadingCache 的方法是调用 get(k) 方法,这个方法如果查询到已经缓存的值会立即返回,否则使用缓存的 CacheLoader 自动加载一个新值到缓存并返回。通过CacheBuilder类的maximumSize方法指定Cache最多可以存储1000个对象,然后调用Cache的put方法向其中添加了第1001个对象时,导致了第一条对象记录被删除。当第一次调用 get() 方法时,如果 value 不存在则会触发 load() 方法,load 方法不能返回 null,否则会报错。

weixin_50993955的博客 1292

基于python的排课表系统,用python编写排课系统

实现一个具体的课程管理系统。(5) 学生可以查看自己选上的课的成绩,以及各个课程的得分、绩点,并给出其平均绩点,但不允许查看年级其他人的成绩。(2) 学生定制专属课表,可以查看整个学期本人的课程安排,包括课程名称、上课时间、上课地点、任课老师、学分等等。(13) 系主任可以查看整个系的全部学生以及他们的平均绩点,可以对每个班进行排名,或者也可以对整个系进行排名。(15) 系主任可以查看整个学期所开设的所有课程,包括这些课程的全部详细信息。(1) 学生基本信息的查询,学生可以对本人的系统登陆密码进行修改。

yang0728y的博客 2000

Guava LoadingCache详解及工具类

缓存,在我们日常开发中是必不可少的一种解决性能问题的方法。简单的说,cache 就是为了提升系统性能而开辟的一块内存空间。  缓存的主要作用是暂时在内存中保存业务系统的数据处理结果,并且等待下次访问使用。在日常开发的很多场合,由于受限于硬盘IO的?  缓存在很多系统和架构中都用广泛的应用,例如:  1.CPU缓存  2.操作系统缓存  3.本地缓存  4.分布式缓存  5.HTTP缓存  6...

丶自定义 5617

论文阅读4——一种宽频带圆极化微带天线的设计

论文阅读——一种宽频带圆极化微带天线的设计(现在才觉得开始弄懂了一点,懂了一点之后又更糊涂了……)

weixin_64612422的博客 2317

大唐豪侠服务器列表文件格式错误,关于大唐豪侠一键端的一些问题心得

有人问了大唐豪侠架设方面的问题,我简单研究了一下,做了个整理,现放出来大家一起研究讨论,我是把服务端的解压到了D盘里面了。第一个问题:架设成功后,运行客户端提示获取服务器列表失败,这个原因是因为压缩的一键端是整理过的,我们需要把服务器列表等解压出来,详见截图首先到这个目录里面找到package.zip,找到这个以后这里要注意,解压路径里面一定不要有package,这个解压成功后就有了服务列表等...

weixin_42530536的博客 1528

Guava自加载缓存LoadingCache

LoadingCache基础LoadingCache基础LoadingCache是Guava中一个提供自动加载功能的缓存接口。它允许咱们通过一个CacheLoader来指定如何加载缓存。这就意味着,当咱们尝试从缓存中读取一个值,如果这个值不存在,LoadingCache就会自动调用预定义的加载机制去获取数据,然后将其加入到缓存中。运行内容如下解释:在这个例子里,创建了一个简单的LoadingCache实例。通过方法获取一个缓存项时,如果这个项不存在,CacheLoader会自动加载一个新值。

weixin_43851023的博客 2176

21天教你学会PCIe专栏(21)--总结和展望

回顾整个课程的主要内容和知识点总结PCIe技术的发展历程和现状展望PCIe技术的未来发展趋势和应用方向分享学习心得和实践经验1.1 课程回顾基础知识:了解了PCIe的基本概念、发展历程、标准和规范。分层结构:学习了PCIe的物理层、数据链路层和事务层的结构和功能。功能和特性:掌握了PCIe的热插拔、电源管理、多功能设备等高级功能。高速接口技术:学习了PCIe 4.0、5.0和未来趋势。实际案例分析:分析了GPU加速器和存储设备的PCIe设计和优化。常见问题和解决方案。

xiaoheshang_123的博客 672

谷歌Guava LoadingCache介绍

在工作中,加Cache是非常常见的一种性能优化手段,操作系统底层、计算机硬件层为了性能优化加了各种各样的Cache,当然大多数都是对应用层透明的。但如果你想在应用层加Cache的话,可能就需要你自己实现了。其实在Java环境下,Cache有各种各样的选择,比如最初级的你可以直接用HashMap实现一个Cache,不过你得自己关注下数据加载和淘汰的策略。

xindoo 2337

基于 DnLUT 超高效的彩色图像去噪(CVPR 2025),本地部署 DnLUT 模型,图像去噪算法训练和推理,全网最详细教程

基于 DnLUT 超高效的彩色图像去噪(CVPR 2025),本地部署 DnLUT 模型,图像去噪算法训练和推理,全网最详细教程

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 365

uni-app - 刘海屏(Iphone X)底部横杠(——)白色区域块(安全距离处更改颜色)解决方案

场景:因为要做无tabbar页面,所以在配置文件中取消底部导航栏。取消后,iPhoneX(刘海屏)底部就出现如下图所示白色安全区域问题:这个问题,是因为uni-app默认会为iPhoneX(刘海屏)留出安全距离,这个安全距离的背景色就是白色。所以,如果你将页面颜色设置为其他颜色,就会与默认白色冲突。有2个不同的解决方案(原生占位),虽然原理是一样,但使用的场景不同,请根据您的需求来采纳。

🏆 前端领域优质创作者 1万+

【Java】guava(一)LoadingCache使用及原理

缓存按照进程可以分为本地缓存和非本地缓存。本地缓存会把数据存储在程序的进程内存中,比如这里的guava。非本地缓存通常由另一个进程维护缓存,与应用程序是分割的,会涉及网络io,比如redis。 redis这类缓存大家应该比较熟悉,这里不做探讨。 本地缓存使用起来更加轻量,且因为没有网络io,效率更快。但是缺点也很明显,缓存大小受限,否则会吃掉项目本身运行时的内存。如果我们的缓存大小不多,且没有...

u010900754的专栏 3660

【功能安全】【AutoSAR】程序流监控机制

目录 一、程序流执行的常见故障 二、程序流的监控机制 1、监控功能的实现 2、三种监督机制 a、生命监测机制 b、截止时间监测机制 c、逻辑程序流监测机制 一、程序流执行的常见故障 与程序流相关的故障,在ISO26262中第六章附录D中定义如下: — 执行阻塞; — 死锁; — 活锁; — 执行时间的不正确分配; — 软件要素间的不正确同步。 二、程序流的监控机制 1、监控功能的实现 AutoSAR中对程序流的监控功能的实现主要有“看门狗”栈(Watchdog Stack).

专注汽车软件开发、AutoSAR、车载以太网、SOA、EE架构。 4519

Java-189 Guava Cache 源码剖析:LocalCache、Segment 与 LoadingCache 工作原理全解析

对 LocalCache、Segment、CacheBuilder、LoadingCache 等核心组件做了系统梳理。首先通过体系类图理清 CacheBuilder、CacheLoader、Cache / LoadingCache 与 LocalCache 之间的职责边界:构建在 Builder,数据结构与并发控制在 LocalCache / Segment,自动加载由 LoadingCache+CacheLoader 负责。随后从源码层面对 LocalCache 的 Segment 结构、Referenc

谢谢你的喜欢 我们一起无限进步 1663

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Guava LoadingCache用法

1.Guava中的cache cache在任何系统中都是一种被广泛使用的数据中间件。对于小规模的缓存数据,Guava中的cache会很实用,使用得也很多。下面我们就针对Guava中的cache做个简单分析。 /** * A semi-persistent mapping from keys to values. Cache entries are manually added using * {@link #get(Object, Callable)} or {@link #put(Object, Ob

bitcarmanlee的博客 2580

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Guava的LoadingCache

一、使用 1)简单初始化: LoadingCache<Long, Long> loadCache = CacheBuilder.newBuilder() .expireAfterWrite(6, TimeUnit.SECONDS) .maximumSize(100) .build(...

iteye_19269的博客 2900

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Guava-Cache

Guava-Cache 摘要 本文只会记录其用法和常用参数,不会分析其原理。 0x01 简介 Guava Cache十分流行,在Apache Calcite等重量级项目中都有使用,比如org.apache.calcite.avatica.jdbc.JdbcMeta: private final Cache&lt;String, Connection&gt; connectionCache; th...

迷路剑客个人博客 396

LoadingCache缓存使用(LoadingCache)

LoadingCache缓存使用(LoadingCache) 一 、引入依赖 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>24.1-jre</version> </dependency> 二、代码案例 package com.example.demo; import

codesweetpotato的博客 7985

Guava Cache源码解析

[TOC] 概述: 本次主要是分析cache的源码,基本概念官方简介即可。 基本类图: 在官方的文档说明中,Guava Cache实现了三种加载缓存的方式: LoadingCache在构建缓存的时候,使用build方法内部调用CacheLoader方法加载数据 在使用get方法的时候,如果缓存不存在该key或者key过期等,则调用get(K, Callable)方式加载数据;

zhanglao33的博客 1135

Java-187 Guava Cache 并发参数与 refreshAfterWrite 实战:LoadingCache 动态加载与自定义 LRU 全解析

concurrencyLevel、refreshAfterWrite 与 LoadingCache 动态加载的底层行为,并结合自定义 LinkedHashMap LRU 实现进行横向对比。通过 Segment 分段锁与 segmentFor(hash) 调用路径,读者可以理解 Guava 如何在多线程场景下复用 ConcurrentHashMap 思路实现高并发缓存访问,并在此基础上用 Runtime.getRuntime().availableProcessors() 等方式按 CPU 数量合理设置并发级

谢谢你的喜欢 我们一起无限进步 1182

使用Guava cache构建本地缓存

什么是本地缓存 在我们的应用中,大部分的计算是昂贵的,而且是可复用的,并且计算结果不会经常发生改变。这时候我们就可以将这些昂贵的计算结果缓存到内存中,下次使用的时候直接取出即可,而不用重新计算。这样可以节省大量的cpu和内存资源,提高系统的吞吐量。 本地缓存作用就是提高系统的运行速度,是一种空间换时间的取舍。它实质上是一个做key-value查询的字典,但是相对于我们常用HashMap它又有以...

HuangTeenC1的博客 768

《Guava缓存工具》学习笔记

Guava通过接口LoadingCache提供了一个非常强大的基于内存的LoadingCache<K,V>。在缓存中自动加载值,它提供了许多实用的方法,在有缓存需求时非常有用。 接口声明 以下是forcom.google.common.cache.LoadingCache<K,V>接口的声明: @Beta @GwtCompatible public interface LoadingCache<K,V> extends Cache<K...

hupengfei_shenyang的博客 438

Guava Cache:原理详解和源码分析

原文路径:Guava Cache:原理详解和源码分析_夜雨落花的博客-优快云博客_guava缓存原理一、基础信息1、maven依赖<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>28.1-jre</version></dependency>...https://blog.csdn.n

L_D_Y_K的博客 1182

【干货分享】Guava LoadingCache 使用指南:打造高性能本地缓存

本文介绍了基于Guava LoadingCache实现的缓存管理器,其核心特性包括自动加载、灵活的缓存配置和批量操作。通过CacheBuilder可配置缓存容量、过期时间及统计功能,采用懒加载机制仅在首次访问时从数据源加载数据,支持基于时间和容量的自动回收策略。该方案适用于高频访问、计算成本高且对数据一致性要求不严格的场景,如用户信息缓存,能有效提升系统性能。文章还详细解析了其工作原理和关键特性,包括线程安全、统计功能和异常处理机制。

qq_33651286的博客 619

Guava精讲(三)-Caches,同步DB数据到缓存

在开发中,我们经常需要从数据库中读取数据并进行频繁的读取操作。缓存在各种场景中都有运用,例如,当一个值的计算或检索成本很高,而且在某个输入中需要多次使用该值时,就应该考虑使用缓存,因此将数据缓存在内存中可以显著提高应用程序的性能。问题描述假设我们正在开发一个电子商务网站,需要频繁地显示商品信息。商品信息存储在数据库中,并且我们希望将其缓存在内存中,以提高网站的响应速度和性能。缓存与 ConcurrentMap 相似,但又不完全相同。

所寫即所思|一个阿里质量人对测试的所感所悟。 2245

19、LoadingCache

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34122810的博客 705
上一篇: JUC中多线程的集合——ConcurrentLinkedQueue
下一篇: 数据库中的MVCC机制
define_us
博客等级 码龄14年 157粉丝 531原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值