java中的缓存技术可以分为远端缓存和本地缓存,其中远端缓存常用的技术有redis、memcache等,而本地缓存常用的技术有HashMap、Guava Cache、Caffeine、Encache。下面聊一下Caffeine缓存技术及简单应用。
1、HashMap
通过Map的底层方式,直接将需要缓存的对象放在内存中。
不需要引入第三方jar包,但需要对HashMap进行封装研发,添加缓存淘汰策略等,如自定义LRU策略的缓存类。
public class LRUCache extends LinkedHashMap {
/**
* 可重入读写锁,保证并发读写安全性
*/
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private Lock readLock = readWriteLock.readLock();
private Lock writeLock = readWriteLock.writeLock();
/**
* 缓存大小限制
*/
private int maxSize;
public LRUCache(int maxSize) {
super(maxSize + 1, 1.0f, true);
this.maxSize = maxSize;
}
@Override
public Object get(Object key) {
readLock.lock();
try {
return super.get(key);
} finally {
readLock.unlock();
}
}
@Override
public Object put(Object key, Object value) {
writeLock.lock();
try {
return super.put(key, value);
} finally {
writeLock.unlock();
}
}
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return this.size() > maxSize;
}
}
2、Caffeine
Guava Cache是由Google开源的基于LRU替换算法的缓存技术,但Guava Cache被Caffeine全面超越而被取代。
Caffeine采用了W-TinyLFU(LUR和LFU的优点结合)开源的缓存技术。缓存性能接近理论最优,属于是Guava Cache的增强版。
public class CaffeineCacheTest {
public static void main(String[] args) throws Exception {
// 创建cache
Cache<String, String> loadingCache = Caffeine.newBuilder()
// cache的初始容量
.initialCapacity(5)
// cache最大缓存数
.maximumSize(10)
// 设置写缓存后n秒钟过期
.expireAfterWrite(10, TimeUnit.SECONDS)
// 设置读写缓存后n秒钟过期,实际很少用到,类似于expireAfterWrite
//.expireAfterAccess(10, TimeUnit.SECONDS)
.build();
String key = "key";
// 往缓存写数据
loadingCache.put(key, "v");
// 获取value的值,如果key存在则返回,否则返回null
String ifPresent = loadingCache.getIfPresent("hello");
// 获取value的值,如果key不存在,获取value后再返回
String value = loadingCache.get(key, CaffeineCacheTest::getValueFromDB);
// 打印
System.out.println(value + "," + ifPresent);
// 删除key
loadingCache.invalidate(key);
}
private static String getValueFromDB(String key) {
return "v2";
}
}
3、springboot中使用Caffeine
引入依赖
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
配置缓存配置类
@Configuration
public class CacheConfig {
@Bean
public Cache<String, Object> caffeineCache() {
return Caffeine.newBuilder()
// 设置最后一次写入或访问后经过固定时间过期
.expireAfterWrite(60, TimeUnit.SECONDS)
// 初始的缓存空间大小
.initialCapacity(100)
// 缓存的最大条数
.maximumSize(1000)
.build();
}
}
业务服务类
@Slf4j
@Service
public class UserInfoService {
/**
* 模拟数据库存储数据
*/
private HashMap<String, UserInfo> userInfoMap = new HashMap<>();
@Autowired
private Cache<String, Object> caffeineCache;
public UserInfo getById(Integer id) {
// 先从缓存读取
UserInfo userInfo = (UserInfo) caffeineCache.getIfPresent(String.valueOf(id));
if (userInfo != null){
return userInfo;
}
// 如果缓存中不存在,则从库中查找
userInfo = userInfoMap.get(id);
// 如果用户信息不为空,则加入缓存
if (userInfo != null){
caffeineCache.put(String.valueOf(userInfo.getId()),userInfo);
}
return userInfo;
}
public void addUserInfo(UserInfo userInfo) {
// 加入数据库
userInfoMap.put(String.valueOf(userInfo.getId()), userInfo);
// 加入缓存
caffeineCache.put(String.valueOf(userInfo.getId()),userInfo);
}
public UserInfo updateUserInfo(UserInfo userInfo) {
userInfo.setName("caocao2");
// 将新的对象存储,更新旧对象信息
userInfoMap.put(String.valueOf(userInfo.getId()), userInfo);
// 替换缓存中的值
caffeineCache.put(String.valueOf(userInfo.getId()),userInfo);
return userInfo;
}
}
测试
@SpringBootTest
class SpringbootCacheApplicationTests {
@Autowired
private UserInfoService userInfoService;
@Test
void contextLoads() {
UserInfo userInfo1 = new UserInfo();
userInfo1.setId(1);
userInfo1.setName("caocao");
userInfo1.setAge(21);
userInfo1.setSex(1);
userInfoService.addUserInfo(userInfo1);
UserInfo userInfo2 = userInfoService.getById(1);
System.out.println(userInfo2);
UserInfo userInfo3 = userInfoService.updateUserInfo(userInfo2);
System.out.println(userInfo3);
}
}
感兴趣的小伙伴可以试试(⊙o⊙)?