首页 > 编程语言 >Java 实现缓存的三种方式

Java 实现缓存的三种方式

时间:2024-03-29 22:33:21浏览次数:26  
标签:缓存 Java name public Step 三种 id String

Java 实现缓存的三种方式

文章目录

一、HashMap实现缓存

​ 可以实现简单的本地缓存,但是实际开发中不推荐,我们可以简单模拟一下缓存的实现,

Step-1:实现一个缓存管理类

public class LocalCache {
    public static HashMap<String,String> cache = new HashMap<>();
    
    static {
        String name = UUID.randomUUID().toString();
        LocalCache.cache.put(String.valueOf(1),name);
        System.out.println("id为1的数据添加到了缓存");
    }
}
// 类中有 `static` 修饰的静态代码块,当类被加载的时候就会执行,如有不懂的可以如下博客
// https://blog.csdn.net/weixin_62636014/article/details/136851287

Tips:我们在static中完成了对缓存的初始化,你可以往缓存里面放入初始数据。

Step-2:将缓存管理类交给 Spring 进行管理

@Component
public class LocalCache {
    public static HashMap<String,String> cache = new HashMap<>();
    
    static {
        String name = UUID.randomUUID().toString();
        LocalCache.cache.put(String.valueOf(1),name);
        System.out.println("id为1的数据添加到了缓存");
    }
    
    @PostConstruct
    public void init() {
        String name = UUID.randomUUID().toString();
        LocalCache.cache.put(String.valueOf(2),name);
        System.out.println("id为2的数据添加到了缓存");
    }
}

Tips:在将缓存管理类交给了 Spring进行管理后,在方法上加入@PostConstruct,可以使方法默认执行,注意该注解不是 Spring 框架提供,仅仅是由 Java JDK 提供的,主要是作用于 Servlet生命周期的注解,实现的是在 Bean 初始化之前自定义操作

@PostConstruct 方法在 Bean初始化中的执行顺序

  • Constructor(构造方法)

  • @Autowired(依赖注入)

  • @PostConstruct (注释的初始化方法)

Step-3:编写接口测试缓存

@RequestMapping("test")
public String test(Long id) {
    String name = LocalCache.cache.get(String.valueOf(id));
    if (name != null) {
        System.out.println("缓存中存在,查询缓存");
        System.out.println(name);
        return name;
    }
    System.out.println("缓存中不存在,查询数据库");
    // 查询数据库操作后,queryDataName方法没有写了;
    // 大家可以自己配一下Mybatis和JDBC进行数据库查询,达到效果是从库中查出来 name;
    name = queryDataName(id);
    System.out.println(name);
    LocalCache.cache.put(String.valueOf(id),name);
    return name;
}

public String queryDataName(Long id) {
    String name = UUID.randomUUID().toString();
    return name;
}

Step-4:结果展示

​ 这个是控制台输出,每个人的随机 UUID 不一致,我这个只是一个样例

id为1的数据添加到了缓存
id为2的数据添加到了缓存
缓存中存在,查询缓存
e2eadabe-3c42-4732-b465-e085ea5faf96
缓存中不存在,查询数据库
942ffe92-454f-4046-87e5-53e8b951d2a1

二、guava local cache 实现

TipsGuavaGoogle提供的一套Java工具包,Guava Cache是一套非常完善的本地缓存机制(JVM缓存),工具类就是封装平常常用的方法,不需要你重复造轮子,节省开发人员时间,我们一般需要知道怎么使用。其设计来源于 CurrentHashMap,可以按照多种策略来清理存储在其中的缓存值且保持很高的并发读写性能。

Guava 提供以下方面的能力

  • 集合 [collections]

  • 缓存 [caching]

  • 原生类型支持 [primitives support]

  • 并发库 [concurrency libraries]

  • 通用注解 [common annotations]

  • 字符串处理 [string processing]

  • I/O 等等。

Step-1:导入guava 依赖

		<dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>32.1.3-jre</version>
        </dependency>

Step-2:使用guava创建简单缓存管理类

为了方便展示,这里面使用了5 秒的缓存保留时间。

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class GuavaLocalCache{
    private Cache<String,String> fiveSecondCache = CacheBuilder.newBuilder()
        //设置缓存初始大小,应该合理设置,后续会扩容
        .initalCapacity(10)
        //最大值
        .maximumSize(100)
        //并发数设置
        .concurrencyLevel(5)
        //缓存过期时间,写入后5秒钟过期
        .expireAfterWrite(5,TimeUnit.SECONDS)
        //统计缓存命中率
        .recordStats()
        .build()
        
    public Cache<String,String> getFiveSecondCache() {
        return fiveSecondCache;
    } // 这里就是拿到缓存对象。
    
    public void setFiveSecondCache(Cache<String,String> fiveSecondCache) {
        this.fiveSecondCache = fiveSecondCache;
    }
}

Step-3:使用 guava cache,并尝试统计命中率

public class test {
    @Autowired
    private GuavaLocalCache guavaLocalCache;
    
    @RequestMapping("guavaTest")
    public String guavaTest(Long id) {
        // 获取缓存
        Cache<String,String> fiveSecondCache = guavaLocalCache.getFiveSecondCache();
        // 从缓存中获取对象
        String nameCache = fiveSecondCache.getIfPresent(String.valueOf(id));
        // 缓存中存在
        if (nameCache != null) {
            System.out.println("缓存命中:"+ nameCache + "," + getCacheStats(fiveSecondCache));
            return nameCache;
        }
        // 将数据存入缓存
        System.out.println("缓存未命中," + getCacheStats(fiveSecondCache));
        nameCache = id + "-" + UUID.randomUUID().toString();
        fiveSecondCache.put(String.valueOf(id),nameCache);
        return nameCache;
    }
    
    public String getCacheStats(Cache<String,String> cahce) {
        CacheStats stats = cache.stats();
        return "缓冲命中率:"+stats.hitRate() +" 被清除缓冲数:" + stats.evictionCount();
    }
     
}

三、使用redis实现缓存

TipsRedis (全称: Remote Dictionary Server 远程字典服务)是一个开源的使用 ANSI C语言 编写、支持网络、可基于内存亦可持久化的日志型、 Key-Value数据库 。Redis 一般被用来做缓存用的,它实际上也是一种数据库(非关系型数据库),可以对经常使用到的数据进行存储,也就是大家所说的缓存。官方给出的数据是, Redis 能达到 10w+QPS( 每秒查询速度 ) 。

Tips: 为什么 Redis 的速度比 Mysql 等这种数据快呢?

​ 因为 Redis 存储的是 key-values 格式的数据,时间复杂度是 O(1) ,即直接通过 key 查询对应的 value 。 而如 Mysql 数据库,底层的实现是 B+ 树,时间复杂度是 O(logn)

​ 最重要的一点是,数据库的数据是存储在磁盘中的,而 Redis 是存储在内存当中的,它们的速度差距不言而喻。但 Redis 也支持持久化存储

Step-1:导入Redis 依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

SpringBoot 配置文件中加入设置,我使用的是yml 形式的文件,如果没有密码的话不填就好了

  redis:
    # IP地址
    host: XXX.XXX.XXX.XXX
    # 密码
    password: XXXXXXXX
    # 端口,默认为6379
    port: 6379
    # 数据库索引
    database: 0
    # 连接超时时间
    timeout: 10s
    lettuce:
      pool:
        # 连接池中的最小空闲连接
        min-idle: 1
        # 连接池中的最大空闲连接
        max-idle: 8
        # 连接池的最大数据库连接数
        max-active: 8
        # #连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1ms

Step-2:编写测试接口

public class TestRedis{
    
    // 下面StringRedisTemplate 是一个继承自 RedisTemplate的类
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    @RequestMapping("/redisTest")
    public String redisCacheTest(Long id){
        String name = stringRedisTemplate.opsForValue().get(String.valueOf(id));
        if (name != null){
            System.out.println("缓存中存在,查询缓存");
            System.out.println(name);
            return name;
        }
        System.out.println("缓存中不存在,查询数据库");
        name = id + "-" + UUID.randomUUID().toString();
        System.out.println(name);
        stringRedisTemplate.opsForValue().set(String.valueOf(id),name);
        return name;
    }
}

Step-3:进行接口测试,并使用Redis DeskTop Manager 进行查看

参考文章

  1. 【java缓存、redis缓存、guava缓存】java中实现缓存的几种方式_java缓存cache-CSDN博客
  2. 一篇文章搞定 Redis 基础知识 - 知乎 (zhihu.com)
  3. Java本地缓存技术选型(Guava Cache、Caffeine、Encache) - 掘金 (juejin.cn)
  4. MemCache原理超详细解读(仅学习) - 知乎 (zhihu.com)
  5. PostConstruct注解详细使用说明及理解-CSDN博客
  6. PostConstruct (Java Platform SE 8 ) (oracle.com)
  7. Java开发利器Guava Cache之使用篇 - 掘金 (juejin.cn)
  8. Google guava 工具类的介绍和使用 - 掘金 (juejin.cn)
  9. Redis详细介绍(精简版)_redis 服务 精简-CSDN博客
  10. 初识Redis,看这一篇就够了

标签:缓存,Java,name,public,Step,三种,id,String
From: https://blog.csdn.net/weixin_62636014/article/details/137127159

相关文章

  • Java 抽象类、接口、内部类
    抽象类Java中的抽象类是一种不能被实例化的类,它用于定义子类必须实现的方法和属性。以下是一些关于Java抽象类的关键点:抽象方法:抽象类可以包含抽象方法,这些方法只有声明没有实现,且必须在任何非抽象子类中被覆写实现。构造方法:虽然抽象类不能直接实例化,但它可以有构造方法。......
  • 华为OD机试 - 传递悄悄话(Java & JS & Python & C & C++)
    须知哈喽,本题库完全免费,收费是为了防止被爬,大家订阅专栏后可以私信联系退款。感谢支持文章目录须知题目描述输入描述输出描述解题思路:题目描述给定一个二叉树,每个节点上站一个人,节点数字表示父节点到该节点传递悄悄话需要花费的时间。初始时,根节点所在......
  • 华为OD机试 - 剩余银饰的重量(Java & JS & Python & C & C++)
    须知哈喽,本题库完全免费,收费是为了防止被爬,大家订阅专栏后可以私信联系退款。感谢支持文章目录须知题目描述输入描述输出描述解题思路:题目描述有N块二手市场收集的银饰,每块银饰的重量都是正整数,收集到的银饰会被熔化用于打造新的饰品。每一回合,从中选......
  • 【 Java系列】--基础篇-1、认识接口中的default关键字
    原创:朱老师缘神日记default关键字:是在Java8中引入的新概念,也可称为Virtualextensionmethods——虚拟扩展方法,它与public、private等都属于修饰符关键字,与其它两个关键字不同之处在于default关键字大部分都用于修饰接口。default修饰方法时只能在接口类中使用,在接口中......
  • 【Java 系列】 -- 1、各种服务器
    一、服务器与容器三、各种项目中的容器原创:数据之恋......
  • Javascript Promise用法
    构造Promise现在我们构造一个promise对象:newPromise(function(resolve,reject)){ //要做的事情...}通过新建一个Promise对象好像并没有看出它怎样“更加优雅地书写复杂的异步任务”。我们之前遇到的异步任务都是一次异步,如果需要多次调用异步函数呢?例如,如果我想分三次......
  • JavaSE_方法method 定义时的注意事项 案例分析
    在定义方法时,需要注意以下几个重要事项:1.**方法定义的位置**:方法不能定义在另一个方法内部。2.**方法名及参数列表**:确保方法名的拼写正确,并且参数列表也要准确无误。参数列表包括参数的类型、顺序和数量,这些都必须与方法调用时的实参匹配。3.**返回值类型**:如果方法声明......
  • JavaSE If执行流程 案例分析
      在一个程序执行的过程中,各条语句的执行顺序对程序的结果是有直接影响的。所以,   我们必须清楚每条语句的执行流程。而且,很多时候我们要通过控制语句的执行顺序   来实现我们要完成的功能。      顺序结构(最常用):代码从上往下一行一行执行publicc......
  • javaScript学习笔记
    关于表单验证的简单实践注意点:1.函数的使用如果在script中需要调用某个function,例如checkUserName(),请确保在定义该函数时的写法为usernameInput.onblur=checkUserName;functioncheckUserName(){如果写成usernameInput.onblur=functioncheck......
  • (day 22)JavaScript学习笔记(内置对象1之Number、Math、Date)
    概述         这是我的学习笔记,记录了JavaScript的学习过程。在写博客的时候我会尽量详尽的记录每个知识点。如果你完全没接触过JavaScript,那么这一系列的学习笔记可能会对你有所帮助。    今天学习JavaScript内置的对象,主要是Number、Math、Date。1.内置......