Java缓存技术(java内置缓存,redis,Ehcache,Caffeine的基本使用方法及其介绍)

目录

摘要

[1. Java缓存技术概述](#1. Java缓存技术概述)

1.1定义

[1.2 优势](#1.2 优势)

[1.3 应用场景](#1.3 应用场景)

[2. Java中的内置缓存实现](#2. Java中的内置缓存实现)

[2.1 通过通过HashMap和ConcurrentHashMap实现缓存](#2.1 通过通过HashMap和ConcurrentHashMap实现缓存)

[3. Java缓存框架](#3. Java缓存框架)

[3.1 Redis](#3.1 Redis)

[3.1.1 redis的简介](#3.1.1 redis的简介)

[3.1.4 Redis的工作原理](#3.1.4 Redis的工作原理)

[3.1.5 总结](#3.1.5 总结)

[3.2 Ehcache](#3.2 Ehcache)

[3.2.1 Ehcache的简介](#3.2.1 Ehcache的简介)

[3.2.2 Ehcache的简单使用](#3.2.2 Ehcache的简单使用)

[3.2.3 数据的持久化](#3.2.3 数据的持久化)

[3.2.4 总结](#3.2.4 总结)

[3.3 Caffeine](#3.3 Caffeine)

[3.3.1 Caffeine的简介](#3.3.1 Caffeine的简介)

[3.3.2 Caffeine的简单使用](#3.3.2 Caffeine的简单使用)

[3.3.3 caffeine总结](#3.3.3 caffeine总结)

[4. 报告总结](#4. 报告总结)

摘要

缓存,作为提升系统性能的关键策略,在Java编程环境中扮演着重要角色。本报告聚焦于Java缓存技术,深入探讨了其基本概念、应用场景及多样化实现。

报告首先明确了缓存的定义,并强调了其在读操作频繁、数据计算复杂及数据更新不频繁等场景下的显著优势。在Java中,缓存的实现方式多样,包括利用本地数据结构实现的本地缓存、借助Java标准库提供的缓存功能。

此外,报告还介绍了几个广受欢迎的Java缓存框架,如Redis(高性能分布式内存缓存)、Ehcache(开源Java缓存框架,支持分布式缓存)以及Caffeine(高性能Java内存缓存库,专注于快速响应和高效内存利用)。这些框架各具特色,为开发者提供了丰富的选择空间。

通过本报告的阐述,读者将全面理解Java缓存技术的核心概念、应用场景及实现方式,为优化系统性能提供有力的理论与实践支持。

  1. Java缓存技术概述

1.1定义

缓存是数据访问的加速器,它为数据提供了一个快速的临时栖息地,以减少数据检索的时间消耗。在Java编程中,缓存技术是提升数据处理速度和系统性能的关键工具。它利用内存这一高速存储介质,保存数据的副本,以便快速访问,避免了对较慢存储设备(如硬盘)的频繁访问。

对于需要频繁访问相同数据的应用,Java缓存技术显得尤为重要。它像一座桥梁,连接了对快速数据的需求和较慢的数据存储,减少了对数据源的直接访问,提高了数据处理的效率。

简而言之,Java中的缓存技术是一种高效的数据处理策略,它利用内存的高速访问特性,为应用程序提供了快速的数据检索服务,从而提高了系统的响应速度和用户体验。这种技术不仅减轻了系统的资源负担,还为数据的快速处理和有效利用提供了新的途径。

1.2 优势

  1. 提高响应速度:通过在快速的存储介质中保存数据副本,缓存减少了数据检索时间,从而加快了应用程序的响应速度。

  2. 减轻后端负载:缓存减少了对数据库或其他数据源的访问次数,从而减轻了后端系统的负担。

  3. 提升用户体验:更快的数据访问速度和更流畅的交互显著提升了用户的体验。

  4. 降低成本:缓存减少了对昂贵资源(如数据库查询)的依赖,有助于降低运营成本。

1.3 应用场景

1. 数据库缓存:

  1. 在高并发访问的系统中,数据库压力可能非常大。为了缓解数据库压力,可以使用缓存来存储常用的查询结果。当再次访问这些数据时,可以直接从缓存中读取,而无需查询数据库,从而显著提高系统的响应速度。
  2. 缓存还可以用于存储数据库中的临时数据,如会话信息、用户登录状态等,以避免频繁访问数据库。
  1. Web应用缓存:
  1. 在Web应用中,缓存可以用于存储静态资源(如图片、CSS、JavaScript等)和动态内容(如网页、API响应等)。
  2. 通过缓存静态资源,可以减少对服务器的请求次数,降低服务器负载,提高网页加载速度。
  3. 对于动态内容,可以使用缓存来存储重复的查询结果或计算结果,以减少数据库查询和计算的时间。
  1. 分布式系统缓存:
  1. 在分布式系统中,缓存可以用于实现分布式锁、分布式会话共享等功能。
  2. 通过使用分布式锁,可以确保多个进程或线程在访问共享资源时的同步性。
  3. 分布式会话共享则允许多个服务器共享用户的会话信息,从而提供一致的用户体验。

4. CDN缓存:

  1. 内容分发网络(CDN)中的缓存用于存储和分发静态内容(如图片、视频、音频等)。
  2. 通过将内容缓存在CDN节点上,可以缩短用户访问内容的距离和时间,提高内容的加载速度和可用性。

5. 应用层缓存:

  1. 在应用层,缓存可以用于存储应用程序的临时数据、计算结果或中间状态。
  2. 这有助于减少应用程序对后端服务的请求次数,提高应用程序的响应速度和性能。
  1. 硬件缓存:
  1. 在计算机硬件中,缓存(如CPU缓存、硬盘缓存等)用于加速数据的访问速度。

  2. 通过将常用数据存储在离处理器更近的缓存中,可以减少对慢速存储设备的访问次数,提高系统的整体性能。

  3. Java中的内置缓存实现

2.1 通过通过 HashMap ConcurrentHashMap 实现缓存

Java 提供了多种基础数据结构,其中 HashMap 和 ConcurrentHashMap 特别适合用于构建内存缓存。HashMap 是一个高效的哈希表实现,而 ConcurrentHashMap 则在此基础上进一步优化,专为多线程环境设计,能够提供卓越的并发访问性能。

然而,这些数据结构有一个共同的局限性:它们不支持数据持久化。因此,当应用程序重启时,所有存储在其中的缓存数据都会丢失。

在特定场景下,如果需要对某些操作进行更精细的控制,以确保其原子性,那么 ConcurrentHashMap 可能无法完全满足需求。此时,开发者可能需要考虑使用更复杂的原子操作,或者将 ConcurrentHashMap 与其他并发控制工具(如锁机制)结合使用,以实现所需的数据一致性和完整性。

以下是简单的代码实现:

public class ConcurrentMyCache {
    private Map<String, Object> cache = new ConcurrentHashMap<>();
    public void put(String key, Object value) {
        cache.put(key, value);
    }
    public Object get(String key) {
        return cache.get(key);
    }
    public void remove(String key) {
        cache.remove(key);
    }
    public void clear() {
        cache.clear();
    }
    public int size() {
        return cache.size();
    }
}



public class MyCache  {

    private Map<String, Object> cache = new HashMap<>();

    // 向缓存中放入键值对

    public void put(String key, Object value) {

        cache.put(key, value);

    }

    // 从缓存中获取值

    public Object get(String key) {

        return cache.get(key);

    }

    // 从缓存中移除键值对

    public void remove(String key) {

        cache.remove(key);

    }

    // 清空缓存

    public void clear() {

        cache.clear();

    }

    // 获取缓存大小

    public int size() {

        return cache.size();

    }

}



@SpringBootTest

class CacheTest {

    @Test

    void testCache(){

        ConcurrentMyCache cache=new ConcurrentMyCache();

        cache.put("key1","zhangsan");

        cache.put("key2","lisi");

        System.out.println("key1: " + cache.get("key1"));

        System.out.println("key2: " + cache.get("key2"));



        MyCache cache1=new MyCache();

        cache1.put("key3","wangwu");

        cache1.put("key4","lht");

        System.out.println("key3: " + cache1.get("key3"));

        System.out.println("key4: " + cache1.get("key4"));

    }

}
  1. Java缓存框架

3.1 Redis

3.1.1 redis的简介

Redis是一个完全开源免费的高性能(NOSQL)的key-value数据库。它遵守BSD协议,使用ANSI C语言编写,并支持网络和持久化。Redis拥有极高的性能,每秒可以进行11万次的读取操作和8.1万次的写入操作。它支持丰富的数据类型,包括String、Hash、List、Set和Ordered Set,并且所有的操作都是原子性的。此外,Redis还提供了多种特性,如发布/订阅、通知、key过期等。Redis采用自己实现的分离器来实现高速的读写操作,效率非常高。Redis是一个简单、高效、分布式、基于内存的缓存工具,通过网络连接提供Key-Value式的缓存服务。

Redis可以通过配置文件设置密码参数,这样客户端连接到Redis服务就需要密码验证,从而提高Redis服务的安全性。

3.1.2 redis的简单使用

java 复制代码
@Data

public class User implements Serializable {

    private Integer id;

    private String username;

    private String password;

}

通过自动装配redis里面的RedisTemplate这个类里面的相关配置并且封装方法方便后期使用。

java 复制代码
/**

 * spring redis 工具类

 *

 **/

@SuppressWarnings(value = { "unchecked", "rawtypes" })

@Component

public class RedisCache

{

    @Autowired

public RedisTemplate redisTemplate;

 /**

     * 缓存基本的对象,Integer、String、实体类等

     *

     * @param key 缓存的键值

     * @param value 缓存的值

     */

    public <T> void setCacheObject(final String key, final T value)

    {

        redisTemplate.opsForValue().set(key, value);

    }



    /**

     * 获得缓存的基本对象。

     *

     * @param key 缓存键值

     * @return 缓存键值对应的数据

     */

    public <T> T getCacheObject(final String key)

    {

        ValueOperations<String, T> operation = redisTemplate.opsForValue();

        return operation.get(key);

}

}

3.1.3 redis的主要特征

  • 键值(key-value)型,value支持多种不同数据结构,功能丰富

  • 单线程,每个命令具备原子性

  • 低延迟,速度快(基于内存、IO多路复用、良好的编码)。

  • 支持数据持久化

  • 支持主从集群、分片集群,

  • 支持多语言客户端

3.1.4 Redis的工作原理

  1. 内存存储:数据完全存储在内存中,提供快速的读写访问,时间复杂度接近O(1)。

  2. ****单线程架构:****采用单线程处理请求,避免了多线程带来的上下文切换和锁竞争,简化了并发控制,提高了性能。

  3. ****非阻塞IO:****使用多路复用IO模型,能够非阻塞地处理多个客户端请求,提高了并发处理能力。

  4. ****Lua脚本执行:****支持在Lua脚本中执行命令,允许用户执行复杂的逻辑和操作,增加了操作的灵活性。

3.1.5 总结

Redis是一种高效的内存键值存储系统,广泛用于缓存管理、会话保持及实时数据处理等多种场景。它的优势体现在:极快的读写速度、对数据结构的多样化支持、具备持久化功能(包括RDB快照和AOF日志)、支持分布式部署,以及提供强大的原子操作特性。

3.2 Ehcache

3.2.1 Ehcache 的简介

EhCache是一个高效的纯Java进程内缓存框架,支持单机和分布式缓存,适用于需要快速数据访问的场景。它具备简单易用、快速访问、多种缓存策略(如堆缓存、磁盘缓存、集群缓存)等优点。EhCache的缓存数据有两级,一级是内存,二级是磁盘,当内存不足时,数据可以自动溢出到磁盘,从而解决了容量问题。此外,EhCache还支持缓存数据在虚拟机重启时写入磁盘,以及通过RMI、可插入API等方式进行分布式缓存。

在Spring Boot中,EhCache可以通过配置文件和Bean注入来使用,提供了灵活的缓存策略配置,如缓存对象的最大数量、对象是否永不过期、空闲时间和存活时间等。同时,EhCache还提供了缓存和缓存管理器的侦听接口,支持多缓存管理器实例以及一个实例的多个缓存区域。

虽然EhCache在非集群环境下可能导致敏感数据更新延迟,但它非常适合高QPS场景和小量数据缓存需求。使用EhCache时,建议设置较短的过期时间以保证数据的及时更新。

3.2.2 Ehcache 的简单使用

首先,需要初始化一个缓存管理器(CacheManager),利用它来创建新的缓存或者访问已有的缓存。之后,可以在这些缓存中存储数据,或者从缓存中检索数据。

java 复制代码
@Test

    public void test() {

        // 初始化 CacheManager

        CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()

                // 一个CacheManager可以管理多个Cache

                .withCache("ehcacheDemo",

                        CacheConfigurationBuilder.newCacheConfigurationBuilder(

                                String.class,

                                String.class,

                                // heap相当于设置数据在堆内存中存储的 个数 或者 大小

                                ResourcePoolsBuilder.newResourcePoolsBuilder()

                                        .heap(10, MemoryUnit.MB).build()).build())

                .build(true);

        // 如果 CacheManagerBuilder.build(); 如果没有传参数,需要手动调用init()

        // cacheManager.init();

        // 基于 CacheManager 获取 Cache对象

        Cache<String, String> ehCache = cacheManager.getCache("ehcacheDemo", String.class, String.class);

        // 放去缓存

        ehCache.put("ehcache", "hello ehcache");

        // 取

        System.out.println(ehCache.get("ehcache"));

    }

EhCache 提供了非常灵活和强大的配置选项,这使得它能够适应各种不同的缓存需求。

java 复制代码
<config xmlns="http://www.ehcache.org/v3"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core.xsd">

    <!--定义缓存-->
    <cache alias="squaredNumber"
           uses-template="myTemplate"><!--缓存使用的缓存模板的名称-->
        <key-type>java.lang.Integer</key-type>
        <value-type>java.lang.Integer</value-type>
        <heap unit="entries">10</heap>
    </cache>

    <!--定义缓存模板-->
    <cache-template name="myTemplate">
        <expiry>
            <ttl unit="seconds">60</ttl><!--缓存项的过期策略,60秒过期-->
        </expiry>
    </cache-template>
</config>

3.2.3 数据的持久化

Ehcache还可以将数据落地本地磁盘,这样的话,当服务重启后,依然会从磁盘反序列化数据到内存中,实现数据的持久化代码如下:

java 复制代码
@Test
public void test1() {
    // 声明存储位置
    String path = "D:\\ehcache";
    // 初始化 CacheManager
    CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
            // 设置存储位置
            .with(CacheManagerBuilder.persistence(path))
            // 一个CacheManager可以管理多个Cache
            .withCache("ehcacheDemo",
                    CacheConfigurationBuilder.newCacheConfigurationBuilder(
                            String.class,
                            String.class,
                            // heap相当于设置数据在堆内存中存储的 个数 或者 大小
                            ResourcePoolsBuilder.newResourcePoolsBuilder()
                                    // 堆内内存
                                    .heap(10, MemoryUnit.MB)
                                    // 堆外内存
                                    // off-heap大小必须 大于 heap 设置的内存大小
                                    .offheap(15,MemoryUnit.MB)
                                    // 磁盘存储,记得添加true,才能正常持久化,并且序列号以及反序列化
                                    // disk大小必须 大于 off-heap 设置的内存
                                    .disk(20,MemoryUnit.MB,true)).build())
            .build(true);
    // 如果 CacheManagerBuilder.build(); 如果没有传参数,需要手动调用init()
    // cacheManager.init();
    // 基于 CacheManager 获取 Cache对象
    Cache<String, String> ehCache = cacheManager.getCache("ehcacheDemo", String.class, String.class);
    // 放去缓存
    ehCache.put("ehcache", "hello ehcache");
    // 取
    System.out.println(ehCache.get("ehcache"));
    // 保证数据正常持久化不丢失,记得 close()
    cacheManager.close();
}

3.2.4 总结

EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点。EhCache支持单机缓存和分布式缓存,分布式可以理解为缓存数据的共享,这就导致内存缓存数据量偏小。ehcache缓存非常存储和读取非常快。

3.3 Caffeine

3.3.1 Caffeine 的简介

Caffeine是一个高性能的Java缓存库,它通过精细的数据结构和高效的内存管理,确保了在高并发环境下的快速访问。它提供了丰富的配置选项,包括缓存的最大容量、数据失效策略和自动刷新等,以满足不同应用程序的需求。Caffeine支持同步和异步两种数据加载方式,同步加载会阻塞主线程直到数据加载完成,而异步加载则允许主线程在数据加载时继续执行,从而提高系统的并发性。此外,Caffeine还支持多种过期策略,如基于时间或访问频率,以及注册监听器来监控缓存的变更事件,为应用程序提供了更细粒度的控制和监视。总的来说,Caffeine是一个功能强大、易于使用的缓存解决方案,非常适合需要高性能缓存的Java应用程序。

3.3.2 Caffeine 的简单使用

  1. 缓存加载策略
  1. Cache手动创建

最普通的一种缓存,无需指定加载方式,需要手动调用put()进行加载。需要注意的是put()方法对于已存在的key将进行覆盖,这点和Map的表现是一致的。在获取缓存值时,如果想要在缓存值不存在时,原子地将值写入缓存,则可以调用get(key, k -> value)方法,该方法将避免写入竞争。调用invalidate()方法,将手动移除缓存。

在多线程情况下,当使用get(key, k -> value)时,如果有另一个线程同时调用本方法进行竞争,则后一线程会被阻塞,直到前一线程更新缓存完成;而若另一线程调用getIfPresent()方法,则会立即返回null,不会被阻塞。

java 复制代码
 @Test

    public void test1() {



        Cache<Object, Object> cache = Caffeine.newBuilder()

                //初始数量

                .initialCapacity(10)

                //最大条数

                .maximumSize(10)

                //expireAfterWrite和expireAfterAccess同时存在时,以expireAfterWrite为准

                //最后一次写操作后经过指定时间过期

                .expireAfterWrite(1, TimeUnit.SECONDS)

                //最后一次读或写操作后经过指定时间过期

                .expireAfterAccess(1, TimeUnit.SECONDS)

                //监听缓存被移除

                .removalListener((key, val, removalCause) -> { })

                //记录命中

                .recordStats()

                .build();



        cache.put("1","张三");

        //张三

        System.out.println(cache.getIfPresent("1"));

        //存储的是默认值

        System.out.println(cache.get("2",o -> "默认值"));

}
  1. Loading Cache自动创建

LoadingCache是一种自动加载的缓存。其和普通缓存不同的地方在于,当缓存不存在/缓存已过期时,若调用get()方法,则会自动调用CacheLoader.load()方法加载最新值。调用getAll()方法将遍历所有的key调用get(),除非实现了CacheLoader.loadAll()方法。使用LoadingCache时,需要指定CacheLoader,并实现其中的load()方法供缓存缺失时自动加载。

在多线程情况下,当两个线程同时调用get(),则后一线程将被阻塞,直至前一线程更新缓存完成。

java 复制代码
 //Loading Cache自动创建

    @Test

    public void test2() {



        LoadingCache<String, String> loadingCache = Caffeine.newBuilder()

                //创建缓存或者最近一次更新缓存后经过指定时间间隔,刷新缓存;refreshAfterWrite仅支持LoadingCache

                .refreshAfterWrite(10, TimeUnit.SECONDS)

                .expireAfterWrite(10, TimeUnit.SECONDS)

                .expireAfterAccess(10, TimeUnit.SECONDS)

                .maximumSize(10)

                //根据key查询数据库里面的值,这里是个lamba表达式

                .build(key -> new Date().toString());



        loadingCache.put("1","张三");

        //张三

        System.out.println(loadingCache.getIfPresent("1"));

        //存储的是默认值

        System.out.println(loadingCache.get("2",o -> "默认值"));

}
  1. Async Cache异步获取

AsyncCache是Cache的一个变体,其响应结果均为CompletableFuture,通过这种方式,AsyncCache对异步编程模式进行了适配。默认情况下,缓存计算使用ForkJoinPool.commonPool()作为线程池,如果想要指定线程池,则可以覆盖并实现Caffeine.executor(Executor)方法。synchronous()提供了阻塞直到异步缓存生成完毕的能力,它将以Cache进行返回。

在多线程情况下,当两个线程同时调用get(key, k -> value),则会返回同一个CompletableFuture对象。由于返回结果本身不进行阻塞,可以根据业务设计自行选择阻塞等待或者非阻塞。

java 复制代码
 //Async Cache异步获取

    @Test

    public void test3() {



        AsyncLoadingCache<String, String> asyncLoadingCache = Caffeine.newBuilder()

                .refreshAfterWrite(1, TimeUnit.SECONDS)

                .expireAfterWrite(1, TimeUnit.SECONDS)

                .expireAfterAccess(1, TimeUnit.SECONDS)

                .maximumSize(10)

                .buildAsync(key -> {

                    try {

                        // 模拟数据库查询延迟

                        Thread.sleep(1000);

                    } catch (InterruptedException e) {

                        Thread.currentThread().interrupt();

                    }

                    // 模拟从数据库获取的数据

                    return new Date().toString();

                });



        // 获取缓存中的值

        CompletableFuture<String> future = asyncLoadingCache.get("1");



        // 当获取完成时,打印结果

        future.thenAccept(System.out::println);



        // 等待一段时间,确保异步加载完成

        try {

            Thread.sleep(2000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }
  1. 驱逐策略

驱逐策略在创建缓存的时候进行指定。常用的有基于容量的驱逐和基于时间的驱逐。基于容量的驱逐需要指定缓存容量的最大值,当缓存容量达到最大时,Caffeine将使用LRU策略对缓存进行淘汰;基于时间的驱逐策略如字面意思,可以设置在最后访问/写入一个缓存经过指定时间后,自动进行淘汰。

  1. 基于容量的驱逐(LRU)

基于容量的驱逐是指缓存在达到一定的容量后,会按照最近最少使用(Least Recently Used)的策略自动淘汰掉一些缓存项。这通常用于限制缓存占用的内存空间。

java 复制代码
//基于容量驱逐

    @Test

    public void maximumSizeTest() throws InterruptedException {

        Cache<Integer, Integer> cache = Caffeine.newBuilder()

                //超过10个后会使用W-TinyLFU算法进行淘汰

                .maximumSize(10)

                .evictionListener((key, val, removalCause) -> {



                    System.out.println("淘汰缓存:key:" + key + " val:" + val);

                })

                .build();



        for (int i = 1; i < 20; i++) {

            cache.put(i, i);

        }

        Thread.sleep(500);//缓存淘汰是异步的



        // 打印还没被淘汰的缓存

        System.out.println(cache.asMap());



}
  1. 基于时间的驱逐

基于时间的驱逐是指缓存项在一定时间后自动被淘汰。可以基于最后一次写入时间或者最后一次访问时间来淘汰缓存项。

java 复制代码
    /**

     * 访问后到期(每次访问都会重置时间,也就是说如果一直被访问就不会被淘汰)

     */

    @Test

    public void expireAfterAccessTest() throws InterruptedException {

        Cache<Integer, Integer> cache = Caffeine.newBuilder()

                .expireAfterAccess(1, TimeUnit.SECONDS)

                //可以指定调度程序来及时删除过期缓存项,而不是等待Caffeine触发定期维护

                //若不设置scheduler,则缓存会在下一次调用get的时候才会被动删除

                .scheduler(Scheduler.systemScheduler())

                .evictionListener((key, val, removalCause) -> {

                    log.info("淘汰缓存:key:{} val:{}", key, val);



                })

                .build();

        cache.put(1, 2);

        System.out.println(cache.getIfPresent(1));

        Thread.sleep(3000);

        System.out.println(cache.getIfPresent(1));//null
  1. 刷新机制

Caffeine 缓存库提供了灵活的刷新机制,可以在缓存项即将过期时自动刷新数据,以确保缓存数据的时效性。refreshAfterWrite()表示x秒后自动刷新缓存的策略可以配合淘汰策略使用,注意的是刷新机制只支持LoadingCache和AsyncLoadingCache。

java 复制代码
//刷新机制

    private static int NUM = 0;



    @Test

    public void refreshAfterWriteTest() throws InterruptedException {

        LoadingCache<Integer, Integer> cache = Caffeine.newBuilder()

                .refreshAfterWrite(1, TimeUnit.SECONDS)

                //模拟获取数据,每次获取就自增1

                .build(integer -> ++NUM);



        //获取ID=1的值,由于缓存里还没有,所以会自动放入缓存

        System.out.println(cache.get(1));// 1



        // 延迟2秒后,理论上自动刷新缓存后取到的值是2

        // 但其实不是,值还是1,因为refreshAfterWrite并不是设置了n秒后重新获取就会自动刷新

        // 而是x秒后&&第二次调用getIfPresent的时候才会被动刷新

        Thread.sleep(2000);

        System.out.println(cache.getIfPresent(1));// 1



        //此时才会刷新缓存,而第一次拿到的还是旧值

        System.out.println(cache.getIfPresent(1));// 2

}
  1. 统计

Caffeine 缓存库提供了内置的统计功能,可以帮助开发者监控和调优缓存性能。通过启用统计功能,你可以收集关于缓存操作的详细信息,例如命中率、未命中率、加载次数、加载时间等。

java 复制代码
 //统计

    @Test

    public void requestCount(){

        LoadingCache<String, String> cache = Caffeine.newBuilder()

                //创建缓存或者最近一次更新缓存后经过指定时间间隔,刷新缓存;refreshAfterWrite仅支持LoadingCache

                .refreshAfterWrite(1, TimeUnit.SECONDS)

                .expireAfterWrite(1, TimeUnit.SECONDS)

                .expireAfterAccess(1, TimeUnit.SECONDS)

                .maximumSize(10)

                //开启记录缓存命中率等信息

                .recordStats()

                //根据key查询数据库里面的值

                .build(key -> {

                    Thread.sleep(1000);

                    return new Date().toString();

                });



        cache.put("1", "shawn");

        cache.get("1");



        /*

         * hitCount :命中的次数

         * missCount:未命中次数

         * requestCount:请求次数

         * hitRate:命中率

         * missRate:丢失率

         * loadSuccessCount:成功加载新值的次数

         * loadExceptionCount:失败加载新值的次数

         * totalLoadCount:总条数

         * loadExceptionRate:失败加载新值的比率

         * totalLoadTime:全部加载时间

         * evictionCount:丢失的条数

         */

        System.out.println(cache.stats());

3.3.3 caffeine总结

Caffeine是一个高性能的Java缓存库,它提供了丰富的配置选项和强大的缓存策略,包括最近最少使用(LRU)、最近最不常用(LFU)、先进先出(FIFO)等。它支持自动刷新、定时失效、大小限制和异步加载,确保了数据的时效性和缓存的高效性。Caffeine还内置了统计监控功能,帮助开发者了解缓存性能并进行调优。其线程安全、易于集成和使用,是提升Java应用性能的理想选择。

4. 报告总结

本报告深入探讨了Java缓存技术,包括其定义、优势、应用场景及实现方式。缓存通过在内存中存储数据副本,减少了对慢速存储设备的访问,从而加速了数据检索,提高了系统性能。Java提供了多种缓存实现,如利用HashMap和ConcurrentHashMap构建本地缓存,以及使用Redis、Ehcache和Caffeine等缓存框架。这些框架支持不同的缓存策略,如堆缓存、磁盘缓存、集群缓存,以及自动刷新和过期策略。Caffeine特别受到关注,它是一个高性能的Java缓存库,提供了丰富的配置选项,包括定时失效、大小限制、同步和异步加载,以及多种过期策略。Caffeine还内置了统计监控功能,帮助开发者优化缓存性能。报告通过介绍这些技术和框架,为读者提供了理论与实践相结合的缓存解决方案,以优化系统性能。

相关推荐
cl°14 小时前
【WPF】如何使用异步方法
经验分享·c#·wpf
月落.15 小时前
WPF的行为(Behavior)
wpf
cl°19 小时前
WPF中视觉树和逻辑树的区别和联系
经验分享·学习·c#·wpf
Nita.4 天前
WPF拖拽交互全攻略及实现自定义拖拽控件及数据交换技巧解析
c#·.net·wpf·1024程序员节
凌霜残雪4 天前
WPF+Mvvm案例实战(五)- 自定义雷达图实现
wpf
月落.4 天前
C#WPF的App.xaml启动第一个窗体的3种方式
ui·c#·wpf
月落.4 天前
WPF样式
开发语言·wpf
时光追逐者4 天前
一个基于.NET8+WPF开源的简单的工作流系统
开发语言·microsoft·c#·asp.net·.net·wpf·.netcore
界面开发小八哥4 天前
DevExpress WPF v24.1新版亮点:PDF查看器、富文本编辑器功能升级
.net·wpf·界面控件·devexpress·ui开发
就是有点傻5 天前
WPF中的Binding
大数据·wpf