写点什么

使用软引用实现缓存机制

作者:JAVA活菩萨
  • 2022 年 8 月 06 日
  • 本文字数:2076 字

    阅读完需:约 7 分钟

使用软引用实现缓存机制

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


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


这种情况我们生活种经常会看到,比如访问某 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


public class GoodsBase {


private static Map<String, Goods> base = new HashMap<>();
public static Map<String, Goods> getBase() { return base;}
复制代码


}复制代码然后书写 goodscache 缓存类这里我们需要接触一个新关键字 volatile


使用 volatile 关键字会强制将修改的值立即写入主存;使用 volatile 关键字的话,当主线程修改时,会导致 RunThread 的工作内存中 isRunning 变量的缓存值变得无效。由于 RunThread 的工作内存中缓存变量 isRunning 缓存无效,所以会再次从主存中读取 isRunning 变量值。在 map 里面通过泛型把缓存对象存储在软引用里面(map 里面)


代码如下:


public class GoodsCache {


private volatile static GoodsCache goodsCache;
public GoodsCache(){ this.cache = new HashMap<>();}
public static GoodsCache getGoodsCache() { if(goodsCache == null) { synchronized (GoodsCache.class){ if(goodsCache == null){ goodsCache = new GoodsCache(); } } } return goodsCache;}
// 将缓存对象存储在软引用中private Map<String, SoftReference<Goods>> cache;
// 根据id存储缓存Goods对象public void setCache(Goods goods) { cache.put(goods.getId(), new SoftReference<Goods>(goods)); System.out.println("添加数据到缓存成功");}
// 根据id从缓存中获取对象public Goods getCache(String id) { // 根据id,获取缓存对象的软引用 SoftReference<Goods> softRef = cache.get(id); return softRef == null ? null : softRef.get();}
public void delCache(String id) { cache.remove(id); System.out.println("从缓存删除数据成功");}
复制代码


}复制代码接下来我们书写 goodsservice 代码,来模拟数据库增删改查,不过我们是通过 id 来进行 public class GoodsService {


GoodsCache goodsCache = GoodsCache.getGoodsCache();
public Goods getById(String id){ if(goodsCache.getCache(id) == null){ Goods goods = GoodsBase.getBase().get(id); goodsCache.setCache(goods); System.out.println("从数据库读取数据"); System.out.println(goods.getName()); return goods; } System.out.println(goodsCache.getCache(id).getName()); return goodsCache.getCache(id);}
public void add(Goods goods){ goodsCache.setCache(goods); GoodsBase.getBase().put(goods.getId(), goods); System.out.println("添加数据到数据库");}
public void deleteById(String id){ if(goodsCache.getCache(id) != null){ goodsCache.delCache(id); } GoodsBase.getBase().remove(id);}
复制代码


}复制代码最后我们书写 test 文件


运行结果


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

用户头像

JAVA活菩萨

关注

还未添加个人签名 2022.07.25 加入

还未添加个人简介

评论

发布
暂无评论
使用软引用实现缓存机制_Java_JAVA活菩萨_InfoQ写作社区