IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 缓存(ehcache/guavaCache使用) -> 正文阅读

[Java知识库]缓存(ehcache/guavaCache使用)

ehcache:

实体对象,要序列化(与sring整合)

@Builder
@Data
public class User implements Serializable {
    private Long id;
    private String name;
}

单独使用:

1.引入依赖

<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.6</version>
</dependency>

2.ehcache.xml编写

<?xml version="1.0" encoding="utf-8" ?>
<ehcache
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="https://www.ehcache.org/ehcache.xsd">

    <!--系统属性,C:\Users\86155\AppData\Local\Temp\默认写到磁盘的路径-->
    <diskStore path="java.io.tmpdir"/>

 
    <!--对应net.sf.ehcache.config.CacheConfiguration
        
        memoryStoreEvictionPolicy:驱逐策略,其他值 net.sf.ehcache.store.MemoryStoreEvictionPolicy
        eternal:永不过期
        timeToIdleSeconds:eternal="false"时有效,闲置多久s
        timeToLiveSeconds:eternal="false"时有效,从创建开始计算,存活多久s
        maxElementsOnDisk:localTempSwap时有效,表示最多可以往磁盘中写多少个
        diskExpiryThreadIntervalSeconds:localTempSwap时有效,检查磁盘元素是否失效的时间间隔
        persistence: 当cache中的元素个数=maxEntriesLocalHeap时,localTempSwap写到磁盘
        其他值:net.sf.ehcache.config.PersistenceConfiguration.Strategy
        statistics="true" 开启统计
    -->
    <cache
            name="user_cache"
            
            maxEntriesLocalHeap="1000"
            eternal="false"
            timeToIdleSeconds="600"
            timeToLiveSeconds="6000"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
            statistics="true"
            >
           <persistence strategy="localTempSwap"/>
    </cache>
   <!--默认-->
   <!-- <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>-->
</ehcache>

3.api使用

    /**
     * 单独使用ehcache的api进行编程
     */
    @Test
    public void test1(){
        //定义ehcache的配置文件
        String absPath = "D:\java\idea\IdeaProject2\cache\echache\src\main\resources\ehcache\ehcache.xml";
        //根据ehcache.xml创建cacheManager,管理多个cache,user_info,item_cache...
        CacheManager cacheManager = CacheManager.create(absPath);
        //getCacheNames():获取到cacheManager管理的所有cache
        String[] cacheNames = cacheManager.getCacheNames();
        System.out.println("获取到的cache的名字:" + Arrays.toString(cacheNames));
        //通过cache的名字(我们指定的)获取具体的cache
        Cache userCache = cacheManager.getCache("user_cache");
        
        User user = User.builder().id(123L).name("WMH").build();
        //往userCache中放入一个user,参数:key 对象
        Element element = new Element(user.getId(),user);
        userCache.put(element);

        //通过key取出 缓存的对象
        Element resultEle = userCache.get(123L);
        System.out.println("获取到的resultEle:" + resultEle);
        System.out.println("获取到的resultEle的Value:" + resultEle.getObjectValue());
    }

在这里插入图片描述

与spring集成:

ehcache与spring的集成方式:

  • 1.AnnotationConfigApplicationContext
  • 2.xml

spring的缓存抽象的方式

  • 1.编程式操作
  • 2.使用注解 如@Cacheable

序列化报错解决方式:

  • 1.不序列化到磁盘,在ehcache.xml添加配置
  • 2.序列化到磁盘:user实现序列化接口

引入spring的cache支持

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-web</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-core</artifactId>
</dependency>
<!-- ehcache 相关依赖 -->
<dependency>
	<groupId>net.sf.ehcache</groupId>
	<artifactId>ehcache</artifactId>
</dependency>
<dependency>
    <groupId>cn.pomit</groupId>
	<artifactId>Mybatis</artifactId>
	<version>${project.version}</version>
</dependency>

编程式操作:

1.ehcache.xml配置文件同上,配置cache信息(实体类不序列化到磁盘)

<persistence strategy="none"/>

2.spring-ehcache.xml配置文件

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/cache
           http://www.springframework.org/schema/cache/spring-cache-4.0.xsd">
    <!--相当于CacheManager cacheManager = CacheManager.create(absPath);
		根据ehcache.xml配置文件生成cacheManager-->
    <bean
            id="ehCacheManager"
            class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath:ehcache/ehcache.xml"/>
    </bean>
    <!--对原生的CacheManager进行包装,org.springframework.cache.CacheManager有多个实现-->
    <!--实现方式一:-->
    <bean id="ehCacheCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
        <property name="cacheManager" ref="ehCacheManager"/>
        <!--事务回滚缓存也回滚-->
        <property name="transactionAware" value="true"/>
    </bean>
    <!--实现方式二:-->
      <!--<bean id="concurrentMapCacheManager"  class="org.springframework.cache.concurrent.ConcurrentMapCacheManager">
          <property name="cacheNames">
            <list>
              <value>user_cache</value>
              <value>store_cache</value>
            </list>
          </property>
        </bean>-->

    <!--跟@EnableCaching一样-->
    <cache:annotation-driven proxy-target-class="true" cache-manager="ehCacheCacheManager"/>
</beans>

3.测试使用

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = {"classpath:ehcache/spring-ehcache.xml"})
public class SpringEhcacheTest {
    @Resource
    private CacheManager cacheManager;

    /**
     * 编程式使用
     */
    @Test
    public void test1(){
        Cache userCache = cacheManager.getCache("user_cache");
        //往userCache中放入一个user
        User user = User.builder().id(123L).name("WMH").build();
        //key 对象
        userCache.put(user.getId(),user);
        //获取
        System.out.println("获取的对象:"+userCache.get(123L,User.class));
    }
}

注解式使用:

1.ehcache.xml 文件同上,spring-ehcache.xml,需要注入serviceImpl,也可改为config类

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/cache
           http://www.springframework.org/schema/cache/spring-cache-4.0.xsd ">

    <bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath:ehcache/ehcache.xml"/>
    </bean>
    <bean id="concurrentMapCacheManager" class="org.springframework.cache.concurrent.ConcurrentMapCacheManager">
        <property name="cacheNames">
            <list>
                <value>user_cache</value>
                <value>store_cache</value>
            </list>
        </property>
    </bean>
    <cache:annotation-driven proxy-target-class="true" cache-manager="concurrentMapCacheManager"/>
    <bean class="com.suisui.service.impl.UserServiceImpl"/>
</beans>

2.serviceImpl实现类

@CacheConfig(cacheNames = {"user_cache"})//存放在哪个缓存里
public class UserServiceImpl implements UserService {

    //调用方法:看缓存有没有?有:返回缓存中的结果
    @Override
    @Cacheable(key="#id")
    //spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中
    public User getById(Long id) {
        System.out.println("模拟查询数据库");
        User user = User.builder().id(id).name("哇哈哈").build();
        return user;
    }
}

测试使用:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = {"classpath:ehcache/spring-ehcache.xml"})
public class SpringEhcacheTest {
    @Resource
    private CacheManager cacheManager;

    @Resource
    private UserService userService;
    
    /**
     * 注解使用,看缓存中有没有,有返回缓存中的结果,没有执行方法,并把结果放入缓存
     */
    @Test
    public void test2(){
        User byId = userService.getById(3L);
        System.out.println("信息:"+byId);
        System.out.println("信息2:"+userService.getById(3L));
        System.out.println("信息3:"+userService.getById(3L));
    }
 }

在这里插入图片描述

与springboot集成:

ehcache与springboot整合

  • 1.在application.yml中指定ehcache的配置文件
  • 2.启动类加@EnableCaching

1.配置文件

spring:
  cache:
    ehcache:
      config: classpath:ehcache/ehcache.xml(同上)

2.添加注解@EnableCaching启用注解

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableCaching
public class SpringBootEhcache {
    public static void main(String[] args) {
        SpringApplication.run(SpringApplication.class,args);
    }
}

3.serviceImpl编写

@Service
public class UserServiceImpl implements UserService {

    @Override
    public User getById(Long id) {
        System.out.println("模拟查询数据库");
        User user = User.builder().id(id).name("哇哈哈").build();
        return user;
    }
}

3.使用


@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootEhcacheTest {
    @Resource
    private CacheManager cacheManager;

    @Resource
    private UserService userService;

    @Test
    public void test1(){
        /**
         * 拿到org.springframework.cache.ehcache.EhCacheCacheManager
         * 就可以进行编程式的api使用
         */
        System.out.println(cacheManager.getClass());
        /**
         * 注解式使用
         */
        User byId = userService.getById(3L);
        System.out.println(byId);
        System.out.println(userService.getById(3L));
        System.out.println(userService.getById(3L));
    }
}

在这里插入图片描述

guava cache:

引入依赖

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

单独使用:

单独使用guava的cache,有两种

  • 1.com.google.common.cache.LocalCache.LocalLoadingCache
    特点:缓存中获取不到值,会根据指定的loader进行加载后自动放入缓存
  • 2.com.google.common.cache.LocalCache.LocalManualCache
    特点:类似ehcache
public class LoadingCacheTest {
    @Test
    public void test1() throws Exception {
        LoadingCache<Long, User> loadingCache = CacheBuilder.newBuilder()
                //指定并发级别
                .concurrencyLevel(8)
                //初始化大小,配合concurrencyLevel做分段锁,提高性能
                .initialCapacity(60)
                //缓存中最多可以放多少元素
                .maximumSize(10)
                //从写入开始计算,3s以后过期,会被清除
                .expireAfterWrite(3, TimeUnit.SECONDS)
                //统计命中率
                .recordStats()
                //缓存中的元素被驱逐出去后自动回调到这里
                .removalListener(new RemovalListener<Long, User>() {
                    @Override
                    public void onRemoval(RemovalNotification<Long, User> notification) {
                        Long key = notification.getKey();
                        RemovalCause cause = notification.getCause();
                        System.out.println("key:" + key+",被移出缓存,原因:"+cause);
                    }
                })
                //缓存中获取不到值,会回调到这里
                .build(new CacheLoader<Long, User>() {
                    @Override
                    public User load(Long key) throws Exception {
                        //key:将来loadingCache.get(key)获取不到传来的key
                        //可以在这里进行数据的加载
                        System.out.println("去存储中加载");
                        User user = User.builder().id(key).name("哇哈哈").build();
                        return user;
                    }
                });

        for (long i = 0; i < 20; i++) {
            //get方法抛异常  loadingCache.get(i);
            User user = loadingCache.getUnchecked(999L);
            System.out.println(user);
            TimeUnit.SECONDS.sleep(1);
        }
        System.out.println("统计信息"+loadingCache.stats().toString());

    }
}

在这里插入图片描述

spring/springboot集成:

spring或者springboot集成任何第三方框架:

  • 1.xml或者在@Configuration中@bean
  • 2.springboot:stater或@Configuration中@bean注入

1.编写GuavaCacheManager

/**
 * 因为spring没有自带的CacheManager
 */
public class GuavaCacheManager extends AbstractCacheManager {
    /**
     * 用来加载当前cacheManager管理哪些cache
     * @return
     */
    @Override
    protected Collection<? extends Cache> loadCaches() {
        /**
         * 获取所有的cache
         */
        com.google.common.cache.Cache<Object,Object> userCache = CacheBuilder.newBuilder()
                .maximumSize(100)
                .build();
        //user_cache保证该缓存存在
        GuavaCache guavaUsercache = new GuavaCache("user_cache", userCache);
        //new GuavaCache("book_cache",bookCache);
        Collection<Cache> caches = new LinkedHashSet();
        caches.add(guavaUsercache);
        return caches;
        }
}

2.GuavaCache 编写

/**
 *自定义cache的场景:
 * 1.GuavaCache
 * 2.应用里需要多级缓存,(1.本地缓存+redis缓存,2.自定义cache)
 */
public class GuavaCache implements Cache {
    private String cacheName;
    /**
     * 使用组合模式持有真正的cache对象
     */
    private com.google.common.cache.Cache<Object,Object> internalCache;

    public GuavaCache(String cacheName, com.google.common.cache.Cache<Object, Object> internalCache) {
        this.cacheName = cacheName;
        this.internalCache = internalCache;
    }

    @Override
    public String getName() {
        return cacheName;
    }

    @Override
    public Object getNativeCache() {
        return internalCache;
    }

    @Override
    public ValueWrapper get(Object key) {
        Object obj = internalCache.getIfPresent(key);
        if(obj != null){
            //返回ValueWrapper的默认实现
            return new SimpleValueWrapper(obj);
        }
        return null;
    }

    @Override
    public <T> T get(Object key, Class<T> aClass) {
        throw new RuntimeException("参考get实现");
    }

    @Override
    public <T> T get(Object key, Callable<T> callable) {
        throw new RuntimeException("参考get实现");
    }

    @Override
    public void put(Object key, Object value) {
        internalCache.put(key,value);
    }

    /**
     * 逐出单个
     * @param key
     */
    @Override
    public void evict(Object key) {
        //这里如果是多级缓存,需要完成本地缓存和分布缓存的同步逻辑
        //比如mq
        internalCache.invalidate(key);
    }

    @Override
    public void clear() {
        internalCache.invalidateAll();
    }
}

3.spring-guava-cache.xml

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/cache
           http://www.springframework.org/schema/cache/spring-cache-4.0.xsd">
    <bean id="guavaCacheManager" class="com.suisui.guava.GuavaCacheManager"></bean>
    <cache:annotation-driven proxy-target-class="true" cache-manager="guavaCacheManager"/>
    <bean class="com.suisui.service.impl.UserServiceImpl"/>
</beans>

4.UserServiceImpl编写

@Service
@CacheConfig(cacheNames = {"user_cache"})
public class UserServiceImpl implements UserService {
    /**
     * 调用方法:看缓存有没有?有:返回缓存中的结果
     */
    @Override
    @Cacheable(key="#id")
    //spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中
    public User getById(Long id) {
        System.out.println("模拟查询数据库");
        User user = User.builder().id(id).name("哇哈哈").build();
        return user;
    }
 }

5.测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = {"classpath:guava/spring-guava-cache.xml"})
public class SpringGuavaCacheTest {

    @Resource
    private GuavaCacheManager cacheManager;
    @Resource
    private UserService userService;
  /**
     * 测试自定义CacheManager
     * 1.编写GuavaCache
     * 2.编写GuavaCacheManager
     * 3.配置spring-guava-cache.xml
     */
    @Test
    public void test1(){
        System.out.println(cacheManager.getClass());
        User byId = userService.getById(3L);
        System.out.println(byId);
        System.out.println(userService.getById(3L));
        System.out.println(userService.getById(3L));
    }
}

在这里插入图片描述

自定义KeyGenerator:

1.编写MyKeyGenerator

/**
 * 缓存的key,使用spel书写
 * 1.有时候参数写的比价麻烦
 * 2.都要写key对应的表达式,可能会忘记写
 * 3.不写使用配置的keyGenerator,写了以写的为准
 */
public class MyKeyGenerator implements KeyGenerator {
    @Override
    public Object generate(Object target, Method method, Object... params) {
        StringBuilder finalKey = new StringBuilder();
        //方法全限定名
        finalKey.append(target.getClass().getName()).append(".")
                .append(method.getName()).append(".");
        if(params == null || params.length == 0){
            return finalKey.append(0).toString();
        }
        for(Object param :params){
            if(param == null){
                finalKey.append("null");
            }else if(ClassUtils.isPrimitiveArray(param.getClass())){
                //一个参数为int[] 等八种基本类型组成数组,不包含包装类
                int length = Array.getLength(param);
                for (int i = 0; i < length; i++) {
                    finalKey.append(Array.get(param,i)).append(",");
                }
            }else if(ClassUtils.isPrimitiveOrWrapper(param.getClass()) || param instanceof String){
                //isPrimitiveOrWrapper:true:八种基本类型+void+8种基本类型的包装类型+字符串
                finalKey.append(param);
            }else{
                //序列化
                String paramStr = JSON.toJSONString(param);
                //对字符串生成hash
                long murmurHash = Hashing.murmur3_128().hashString(paramStr, StandardCharsets.UTF_8).asLong();
                finalKey.append(murmurHash);
            }
            //分隔多个参数
            finalKey.append("-");
        }
        System.out.println("最终的Key: "+finalKey);
        return finalKey;
    }
}

2.spring-guava-cache.xml(指定KeyGenerator)

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/cache
           http://www.springframework.org/schema/cache/spring-cache-4.0.xsd">
    <bean id="guavaCacheManager" class="com.suisui.guava.GuavaCacheManager"></bean>
    <bean id="keyGenerator" class="com.suisui.key.MyKeyGenerator"></bean>
    <cache:annotation-driven proxy-target-class="true" cache-manager="guavaCacheManager" key-generator="keyGenerator"/>
    <bean class="com.suisui.service.impl.UserServiceImpl"/>
</beans>

3.UserServiceImpl编写

@Service
@CacheConfig(cacheNames = {"user_cache"})
public class UserServiceImpl implements UserService {
    /**
     *key遵循spring的spel语法,1.有时候参数写的比价麻烦2.都要写key对应的表达式
     */
    @Override
    @Cacheable
    public User getUser(User queryParam, int[] arr,String str) {
        System.out.println("模拟查询数据库--测试自定义keyGenerator");
        User user = User.builder().id(99L).name("java 数据").build();
        return user;
    }
}

4.测试自定义keyGenerator

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = {"classpath:ehcache/spring-guava-cache.xml"})
public class SpringGuavaCacheTest {

    @Resource
    private GuavaCacheManager cacheManager;
    @Resource
    private UserService userService;

    /**
     * 测试自定义的keyGenerator
     */
    @Test
    public void test2(){
        User queryParam = User.builder().id(66L).name("碎金").build();
        int[] arr = new int[2];
        arr[0] = 1;
        arr[1] = 2;
        String str = "abc";
        User user = userService.getUser(queryParam, arr,str);
        System.out.println(user);
        System.out.println(userService.getUser(queryParam, arr,str));
        System.out.println(userService.getUser(queryParam, arr,str));
    }
}

在这里插入图片描述

自定义缓存

缓存应具备的功能:

1.最大能放多少个,溢出淘汰的机制:FIFO,LRU,LFU

  • 淘汰最先放入缓存的数据,即FIFO(First In First Out)
  • 淘汰最久未使用的数据,即 LRU(Least Recently Used)
  • 淘汰使用频率低的数据,即LFO(Least Frequently Used)

2.过期需要清除
3.内存敏感(不能因为缓存的原因导致业余程序的OOM)
4.线程的安全
5.统计命中率
6.序列化扩展

LRU淘汰策略的实现

基于LinkedHashMap实现

LinkedHashMap测试:

 @Test
    public void test1(){
        /*int initialCapacity, 初始大小
        *float loadFactor,载荷系数
        boolean accessOrder  设为true,根据访问时间排序*/
        // LinkedHashMap会根据访问时间动态排序
        LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>(16,0.75f,true);
        linkedHashMap.put("a","a_value");
        linkedHashMap.put("b","b_value");
        linkedHashMap.put("c","c_value");
        System.out.println(linkedHashMap);
        String bValue = linkedHashMap.get("b");
        System.out.println("b的值:" + bValue);
        System.out.println(linkedHashMap);
    }

在这里插入图片描述
1.缓存功能接口

/**
 * 定义基本的缓存功能接口
 */
public interface Cache {
    void put(Object key,Object value);
    void remove(Object key,Object value);
    void clear();
    Object get(Object key);
    int size();
}

2.基于LinkedHashMap实现缓存:

public class CacheVersion1 implements Cache {
    //缓存容量
    private int capacity;
    //通过组合关系持有一个内部的真正缓存对象
    private InternalCache internalCache;

    public CacheVersion1(int capacity) {
        this.capacity = capacity;
        internalCache = new InternalCache(capacity);
    }

    private static class InternalCache extends LinkedHashMap<Object,Object>{
        private int capacity;
        public InternalCache(int capacity) {
            super(16,0.75f,true);
            this.capacity = capacity;}
        /**
         *在java.util.LinkedHashMap#afterNodeInsertion(boolean)回调
         * @param eldest
         * @return true:清除排在最前面的元素,false不清除
         */
        @Override
        protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
            return size() > capacity;
        }
    }
    @Override
    public void put(Object key, Object value) {
        internalCache.put(key,value);
    }

    @Override
    public void remove(Object key, Object value) {
        internalCache.remove(key,value);
    }

    @Override
    public void clear() {
        internalCache.clear();
    }

    @Override
    public Object get(Object key) {
       return internalCache.get(key);
    }

    @Override
    public int size() {
        return internalCache.size();
    }

    @Override
    public String toString() {
        return "CacheVersion1{" +
                "capacity=" + capacity +
                ", internalCache=" + internalCache +
                '}';
    }
}

3.测试

    @Test
    public void test2(){
        CacheVersion1 cache = new CacheVersion1(3);
        cache.put("a","a_value");
        cache.put("b","b_value");
        cache.put("c","c_value");
        System.out.println(cache);
        //演示访问一次,改变排序
        String bValue = (String)cache.get("b");
        System.out.println("b的值:"+bValue);
        //重新排序
        System.out.println(cache);
    }

在这里插入图片描述

基于LinkedList实现

缓存接口同上,基于LinkedList实现缓存:

/**
 * 基于linkedList实现LRU驱逐算法
 */
public class CacheVersion2 implements Cache {
    //缓存容量
    private int capacity;
    //用来维护缓存key的顺序
    private LinkedList<Object> keyList;
    //通过组合关系持有一个内部的真正缓存对象
    private Map<Object,Object> internalCache;

    public CacheVersion2(int capacity) {
        this.capacity = capacity;
        internalCache = new HashMap<>();
        keyList = new LinkedList<>();
    }


    @Override
    public void put(Object key, Object value) {
        //调用put此方式时,已存在的元素个数==容量
        if(size() == capacity){
            //移除第一个(get()中设置,最后访问的在最后)
            Object firstKey = keyList.removeFirst();
            internalCache.remove(firstKey);
        }
        //加入当前key
        keyList.addLast(key);
        internalCache.put(key,value);
    }

    @Override
    public void remove(Object key, Object value) {
        keyList.remove(key);
        internalCache.remove(key,value);
    }

    @Override
    public void clear() {
        keyList.clear();
        internalCache.clear();
    }

    @Override
    public Object get(Object key) {
        //true:key存在,false:key在keyList中不存在
        boolean removeResult = keyList.remove(key);
        if(removeResult){
            Object value = internalCache.get(key);
            //把现在访问的key排序,最后访问的放在最后
            keyList.addLast(key);
            return value;
        }
        return null;
    }

    @Override
    public int size() {
        //sinternalCache.get(key)
        return keyList.size();
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (Object key : keyList) {
            sb.append("key").append(key).append(",")
                    .append("value:").append(internalCache.get(key))
                    .append("-");
        }
        return sb.toString();
    }
}

测试:

    @Test
    public void test3(){
        CacheVersion2 cache = new CacheVersion2(3);
        cache.put("a","a_value");
        cache.put("b","b_value");
        cache.put("c","c_value");
        System.out.println(cache);
        //演示访问一次,改变排序
        String bValue = (String)cache.get("b");
        System.out.println("b的值:"+bValue);
        //重新排序
        System.out.println(cache);
        cache.put("d","d_value");
        System.out.println(cache);
    }

在这里插入图片描述

内存敏感能力的实现

强引用:只要有引用就不会被回收,基本OOM
软引用:gc且堆内存吃紧时才会被回收(适合)
弱引用:每次gc都会被回收
虚引用:随时都有可能被回收

  /*测试强引用OOM*/
    @Test
    public void test() throws InterruptedException {
        CacheVersion1 cache = new CacheVersion1(99999);
        for (int i = 1; i < Integer.MAX_VALUE; i++) {
            Dept dept = new Dept((long)i);
            cache.put(dept.getId(),dept);
            TimeUnit.SECONDS.sleep(1);
        }
    }

设置参数:
在这里插入图片描述
结果:
在这里插入图片描述
内存敏感实现(get()/put()):

/**
 * 基于linkedHashMap实现LRU驱逐算法+内存敏感
 */
public class CacheVersion1final implements Cache {
    //缓存容量
    private int capacity;
    //通过组合关系持有一个内部的真正缓存对象
    private InternalCache internalCache;

    public CacheVersion1final(int capacity) {
        this.capacity = capacity;
        internalCache = new InternalCache(capacity);
    }

    private static class InternalCache extends LinkedHashMap<Object,Object>{
        private int capacity;
        public InternalCache(int capacity) {
            super(16,0.75f,true);
            this.capacity = capacity;}
        /**
         *在java.util.LinkedHashMap#afterNodeInsertion(boolean)回调
         * @param eldest
         * @return true:清除排在最前面的元素,false不清除
         */
        @Override
        protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
            return size() > capacity;
        }
    }
    @Override
    public void put(Object key, Object value) {
        SoftReference<Object> softReference = new SoftReference<>(value);
        internalCache.put(key,softReference);
    }

    @Override
    public void remove(Object key, Object value) {
        internalCache.remove(key,value);
    }

    @Override
    public void clear() {
        internalCache.clear();
    }

    @Override
    public Object get(Object key) {
        Object o = internalCache.get(key);
        if(o == null){
            return null;
        }
        SoftReference<Object> softReference = (SoftReference<Object>) o;
        //返回引用的真实对象
        return softReference.get();
    }

    @Override
    public int size() {
        return internalCache.size();
    }

    @Override
    public String toString() {
        return "CacheVersion1{" +
                "capacity=" + capacity +
                ", internalCache=" + internalCache +
                '}';
    }
}

测试弱引用:

    /*测试软引用内存敏感*/
    @Test
    public void test5() throws InterruptedException {
        CacheVersion1final cache = new CacheVersion1final(99999);
        for (int i = 1; i < Integer.MAX_VALUE; i++) {
            System.out.println("放入第"+i+"个");
            Dept dept = new Dept((long)i);
            cache.put(dept.getId(),dept);
        }
    }

在这里插入图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-09-24 20:42:45  更:2022-09-24 20:46:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 9:00:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码