java使用软引用实现缓存机制示例
作者:My2538772270 发布时间:2021-08-26 18:06:12
“读多写少”是大部分项目的一个特点。例如“购物”,总是看的人多(读)、买的人少(写)。因此,如果能减少“读”请求的次数,就能减少服务端的压力。最直接的减少“读”请求次数的方法就是使用缓存。
软引用和强引用
对于同一个读请求,只需要在第一次访问时从数据库中查询数据,并将查询到的数据保存到缓存中,之后的查询请求就可以直接在缓存中获取,从而减少对数据库的访问次数。
这种情况我们生活种经常会看到,比如访问某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模拟数据库增删改查
接下来我们书写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");
是从缓存中直接读取的数据,也可以看出,其实用软引用实现缓存机制,读取的对象是同一个对象。
来源:https://juejin.cn/post/7128680512868057118


猜你喜欢
- 提示:建议一定要看后面的@RequestBody的核心逻辑源码以及六个重要结论!本文前半部分的内容都是一些基本知识常识,可选择性跳过。声明:
- 前言图文并茂的内容往往让人看起来更加舒服,如果只是文字内容的累加,往往会使读者产生视觉疲劳。搭配精美的文章配图则会使文章内容更加丰富,增加文
- 详解Kotlin的空指针处理Kotlin的空指针处理相比于java有着极大的提高,可以说是不用担心出现NullPointerExceptio
- 在线程中有两种常用的方法,能够通过数组实现相应的功能,但除此之外在区别上也是很明显的。本篇就其中的代表方法ArrayList和Vector进
- 简介最近学了java基础后对以前不会写的作业深有感触,想起以前各种在网上找资料找别人的代码参考,所以今天特地写了了简单的基于控制台的学生信息
- 目录概述LRU 的原理LRU 算法的实现LRU 算法描述LRU 算法代码实现方法一方法二方法三总结概述LRU 算法全称为 Least Rec
- 使用new操作符来创建对象,其背后到底发生了什么?有一个父类Animal,Dog派生于Animal。class Program
- 本文实例为大家分享了java实现斗地主发牌系统的具体代码,供大家参考,具体内容如下玩家类package com.softeem.exampl
- 一. spring配置文件:application.xml<?xml version="1.0" encoding
- 欣赏一下我们清爽的界面吧~如果是只用activity来制作这样的东西简直是太小儿科了,此处我们当然用的是service首先我们先上servi
- 本文实例为大家分享了Android实现炫酷进度条的具体代码,供大家参考,具体内容如下下面我们来实现如下效果:第一步:创建attrs文件夹,自
- 本文实例为大家分享了Unity调取移动端的麦克风进行录音并播放的具体代码,供大家参考,具体内容如下1.对MicroPhone类的理解对麦克风
- //方法一//须添加对System.Web的引用//using System.Web.Security;/// <summary>
- 要求: * 判断用户输入的年份是平年还是闰年实现代码:import java.util.Scanner;/** * 要
- 目录一、log4j简介1、Loggers2、Appenders3、Layouts二、配置详解1、配置根Logger:2、配置日志信息输出目的
- 这篇文章主要介绍了Spring自定义参数解析器代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋
- 本文实例讲述了C#画笔使用复合数组绘制单个矩形的方法。分享给大家供大家参考。具体实现方法如下:using System;using Syst
- 目录一、事出有因二、解决方案困境三、柳暗花明,终级解决方案第一种实现方案第二种实现方案第三种实现方案四、引发的思考一、事出有因最近有一个场景
- 前言Go语言定义Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thomps
- 前言spring中解析元素最重要的一个对象应该就属于 BeanDefinition了;这个Spring容器中最基本的内部数据结构;它让xml