单机缓存
- ehcache:
- 单独使用:
- 与spring集成:
- 编程式操作:
- 注解式使用:
- 与springboot集成:
- guava cache:
- 单独使用:
- spring/springboot集成:
- 自定义KeyGenerator:
- 自定义缓存
- 缓存应具备的功能:
- LRU淘汰策略的实现
- 基于LinkedHashMap实现
- 基于LinkedList实现
- 内存敏感能力的实现
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" ?>
<ehcachexmlns: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.CacheConfigurationmemoryStoreEvictionPolicy:驱逐策略,其他值 net.sf.ehcache.store.MemoryStoreEvictionPolicyeternal:永不过期timeToIdleSeconds:eternal="false"时有效,闲置多久stimeToLiveSeconds:eternal="false"时有效,从创建开始计算,存活多久smaxElementsOnDisk:localTempSwap时有效,表示最多可以往磁盘中写多少个diskExpiryThreadIntervalSeconds:localTempSwap时有效,检查磁盘元素是否失效的时间间隔persistence: 当cache中的元素个数=maxEntriesLocalHeap时,localTempSwap写到磁盘其他值:net.sf.ehcache.config.PersistenceConfiguration.Strategystatistics="true" 开启统计--><cachename="user_cache"maxEntriesLocalHeap="1000"eternal="false"timeToIdleSeconds="600"timeToLiveSeconds="6000"maxElementsOnDisk="10000000"diskExpiryThreadIntervalSeconds="120"memoryStoreEvictionPolicy="LRU"statistics="true"><persistence strategy="localTempSwap"/></cache><!--默认--><!-- <defaultCachemaxElementsInMemory="10000"eternal="false"timeToIdleSeconds="120"timeToLiveSeconds="120"maxElementsOnDisk="10000000"diskExpiryThreadIntervalSeconds="120"memoryStoreEvictionPolicy="LRU"><persistence strategy="localTempSwap"/></defaultCache>-->
</ehcache>
3.api使用
/*** 单独使用ehcache的api进行编程*/@Testpublic 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管理的所有cacheString[] cacheNames = cacheManager.getCacheNames();System.out.println("获取到的cache的名字:" + Arrays.toString(cacheNames));//通过cache的名字(我们指定的)获取具体的cacheCache 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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/cachehttp://www.springframework.org/schema/cache/spring-cache-4.0.xsd"><!--相当于CacheManager cacheManager = CacheManager.create(absPath);根据ehcache.xml配置文件生成cacheManager--><beanid="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 {@Resourceprivate CacheManager cacheManager;/*** 编程式使用*/@Testpublic void test1(){Cache userCache = cacheManager.getCache("user_cache");//往userCache中放入一个userUser 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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/cachehttp://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 {@Resourceprivate CacheManager cacheManager;@Resourceprivate UserService userService;/*** 注解使用,看缓存中有没有,有返回缓存中的结果,没有执行方法,并把结果放入缓存*/@Testpublic 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 {@Overridepublic 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 {@Resourceprivate CacheManager cacheManager;@Resourceprivate UserService userService;@Testpublic 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 {@Testpublic 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>() {@Overridepublic 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>() {@Overridepublic 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*/@Overrideprotected 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;}@Overridepublic String getName() {return cacheName;}@Overridepublic Object getNativeCache() {return internalCache;}@Overridepublic ValueWrapper get(Object key) {Object obj = internalCache.getIfPresent(key);if(obj != null){//返回ValueWrapper的默认实现return new SimpleValueWrapper(obj);}return null;}@Overridepublic <T> T get(Object key, Class<T> aClass) {throw new RuntimeException("参考get实现");}@Overridepublic <T> T get(Object key, Callable<T> callable) {throw new RuntimeException("参考get实现");}@Overridepublic void put(Object key, Object value) {internalCache.put(key,value);}/*** 逐出单个* @param key*/@Overridepublic void evict(Object key) {//这里如果是多级缓存,需要完成本地缓存和分布缓存的同步逻辑//比如mqinternalCache.invalidate(key);}@Overridepublic 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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/cachehttp://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 {@Resourceprivate GuavaCacheManager cacheManager;@Resourceprivate UserService userService;/*** 测试自定义CacheManager* 1.编写GuavaCache* 2.编写GuavaCacheManager* 3.配置spring-guava-cache.xml*/@Testpublic 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 {@Overridepublic 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);//对字符串生成hashlong 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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/cachehttp://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@Cacheablepublic 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 {@Resourceprivate GuavaCacheManager cacheManager;@Resourceprivate UserService userService;/*** 测试自定义的keyGenerator*/@Testpublic 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测试:
@Testpublic 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不清除*/@Overrideprotected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {return size() > capacity;}}@Overridepublic void put(Object key, Object value) {internalCache.put(key,value);}@Overridepublic void remove(Object key, Object value) {internalCache.remove(key,value);}@Overridepublic void clear() {internalCache.clear();}@Overridepublic Object get(Object key) {return internalCache.get(key);}@Overridepublic int size() {return internalCache.size();}@Overridepublic String toString() {return "CacheVersion1{" +"capacity=" + capacity +", internalCache=" + internalCache +'}';}
}
3.测试
@Testpublic 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<>();}@Overridepublic void put(Object key, Object value) {//调用put此方式时,已存在的元素个数==容量if(size() == capacity){//移除第一个(get()中设置,最后访问的在最后)Object firstKey = keyList.removeFirst();internalCache.remove(firstKey);}//加入当前keykeyList.addLast(key);internalCache.put(key,value);}@Overridepublic void remove(Object key, Object value) {keyList.remove(key);internalCache.remove(key,value);}@Overridepublic void clear() {keyList.clear();internalCache.clear();}@Overridepublic 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;}@Overridepublic int size() {//sinternalCache.get(key)return keyList.size();}@Overridepublic 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();}
}
测试:
@Testpublic 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*/@Testpublic 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不清除*/@Overrideprotected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {return size() > capacity;}}@Overridepublic void put(Object key, Object value) {SoftReference<Object> softReference = new SoftReference<>(value);internalCache.put(key,softReference);}@Overridepublic void remove(Object key, Object value) {internalCache.remove(key,value);}@Overridepublic void clear() {internalCache.clear();}@Overridepublic Object get(Object key) {Object o = internalCache.get(key);if(o == null){return null;}SoftReference<Object> softReference = (SoftReference<Object>) o;//返回引用的真实对象return softReference.get();}@Overridepublic int size() {return internalCache.size();}@Overridepublic String toString() {return "CacheVersion1{" +"capacity=" + capacity +", internalCache=" + internalCache +'}';}
}
测试弱引用:
/*测试软引用内存敏感*/@Testpublic 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);}}