• 使用软引用实现缓存机制


    “读多写少”是大部分项目的一个特点。例如“购物”,总是看的人多(读)、买的人少(写)。因此,如果能减少“读”请求的次数,就能减少服务端的压力。最直接的减少“读”请求次数的方法就是使用缓存。

    软引用和强引用

    对于同一个读请求,只需要在第一次访问时从数据库中查询数据,并将查询到的数据保存到缓存中,之后的查询请求就可以直接在缓存中获取,从而减少对数据库的访问次数。

    这种情况我们生活种经常会看到,比如访问某app某商品,第一次进去会加载一会会,后面继续点击是直接出现。

    根据目前所学知识,我们可以使用 HashMap 在内存级别实现缓存功能。例如,可以使用一个 HashMap 对象保存客户端第一次请求的结果,之后,当客户端再次发起读请求时,就从 HashMap 对象中遍历查询,如果 HashMap 中已经保存过客户要查询的数据,就直接返回,否则再向数据库发起查询请求,并将查询结果保存到 HashMap 中。

    这种缓存的设计思路十分简单,但也存在一个问题:HashMap 中缓存的数据何时被清空?

    内存容量是有限制的,如果永无止尽的向 HashMap 缓存数据,显然会对内存容量带来压力。一种解决方案就是使用 JVM 提供的软引用,实现对 HashMap 中缓存数据的淘汰策略。

    开发中最常使用的是强引用,例如 Goods goods = new Goods() 就创建了一个强引用对象“goods”。只要强引用的作用域没有结束,或者没有被开发者手工设置为 null,那么强引用对象会始终存在于 JVM 内存中。

    而 JVM 提供的软引用就比较灵活:当 JVM 的内存足够时,GC 对待软引用和强引用的方式是一样的;但当 JVM 的内存不足时,GC 就会去主动回收软引用对象。可见,非常适合将缓存的对象存放在软引用中。软引用需要借助 JDK 提供的 java.lang.ref.SoftReference 类来实现。

    项目

    使用idea创建一个maven项目

    结构如下

    首先对Good实体类进行编写。

    要求,goods有属性id,name并书写他的getset方法,以及有参无参构造器。

    这里代码省略。

    然后我们在goodbase里面编写代码,模拟一个数据库

    里面主要有hashmap,并且通过get方法,得到该hashmap

    1. public class GoodsBase {
    2. private static Map<String, Goods> base = new HashMap<>();
    3. public static Map<String, Goods> getBase() {
    4. return base;
    5. }
    6. }

    然后书写goodscache缓存类

    这里我们需要接触一个新关键字volatile

    1. 使用volatile关键字会强制将修改的值立即写入主存;
    2. 使用volatile关键字的话,当主线程修改时,会导致RunThread的工作内存中isRunning变量的缓存值变得无效。
    3. 由于RunThread的工作内存中缓存变量isRunning缓存无效,所以会再次从主存中读取isRunning变量值。

    在map里面通过泛型把缓存对象存储在软引用里面(map里面)

    代码如下:

    1. public class GoodsCache {
    2. private volatile static GoodsCache goodsCache;
    3. public GoodsCache(){
    4. this.cache = new HashMap<>();
    5. }
    6. public static GoodsCache getGoodsCache() {
    7. if(goodsCache == null) {
    8. synchronized (GoodsCache.class){
    9. if(goodsCache == null){
    10. goodsCache = new GoodsCache();
    11. }
    12. }
    13. }
    14. return goodsCache;
    15. }
    16. // 将缓存对象存储在软引用中
    17. private Map<String, SoftReference<Goods>> cache;
    18. // 根据id存储缓存Goods对象
    19. public void setCache(Goods goods) {
    20. cache.put(goods.getId(), new SoftReference(goods));
    21. System.out.println("添加数据到缓存成功");
    22. }
    23. // 根据id从缓存中获取对象
    24. public Goods getCache(String id) {
    25. // 根据id,获取缓存对象的软引用
    26. SoftReference softRef = cache.get(id);
    27. return softRef == null ? null : softRef.get();
    28. }
    29. public void delCache(String id) {
    30. cache.remove(id);
    31. System.out.println("从缓存删除数据成功");
    32. }
    33. }

    接下来我们书写goodsservice代码,来模拟数据库增删改查,不过我们是通过id来进行

    1. public class GoodsService {
    2. GoodsCache goodsCache = GoodsCache.getGoodsCache();
    3. public Goods getById(String id){
    4. if(goodsCache.getCache(id) == null){
    5. Goods goods = GoodsBase.getBase().get(id);
    6. goodsCache.setCache(goods);
    7. System.out.println("从数据库读取数据");
    8. System.out.println(goods.getName());
    9. return goods;
    10. }
    11. System.out.println(goodsCache.getCache(id).getName());
    12. return goodsCache.getCache(id);
    13. }
    14. public void add(Goods goods){
    15. goodsCache.setCache(goods);
    16. GoodsBase.getBase().put(goods.getId(), goods);
    17. System.out.println("添加数据到数据库");
    18. }
    19. public void deleteById(String id){
    20. if(goodsCache.getCache(id) != null){
    21. goodsCache.delCache(id);
    22. }
    23. GoodsBase.getBase().remove(id);
    24. }
    25. }

    最后我们书写test文件

    运行结果

    可以看到第二次运行 goodsService.getById("1"); 是从缓存中直接读取的数据,也可以看出,其实用软引用实现缓存机制,读取的对象是同一个对象。

  • 相关阅读:
    golang数据库连接池参数设置
    【SpringCloud-学习笔记】统一网关Gateway
    【云备份|| 日志 day5】文件热点管理模块
    【微信小程序开发】小程序版的防抖节流应该怎么写
    springboot+vue+elementUI304springboot305springboot农机电招租赁预约平台#毕业设计
    (附源码)spring boot物联网智能管理平台 毕业设计 211120
    win10应用商店怎么重新安装?
    6线SPI传输模式探索
    VK12 打删除标记的操作
    初识生成对抗网络(11)——利用Pytorch搭建WGAN生成手写数字
  • 原文地址:https://blog.csdn.net/LBWNB_Java/article/details/126314867