1、本地jvm缓存
缓存是将数据在整个程序生命周期中存储的内存
作用:
- 能够提升获取数据的效率,避免IO读取耗时操作。
- 被大量的读取,更新频率低。
- 经常变化,但是不会被持久化的。
方式:
使用static关键字开辟内存与搜索时间复杂度O(1)的hashmap来存储数据提高读取效率;然后实现相对应的缓存写入,更新,读取,删除等逻辑
spring-boot提供了一个spring-boot-devtools包,其作用就是在不重启服务的情况下将服务重新部署到服务器上。原理是使用两个类加载器,一个用于加载变化的类文件,一个用于加载未发生变化的类文件,直接对未改变的类文件进行加载极大的提高整个效率。
或者使用Redis作为缓存。
HashMap缺点
- 没有缓存大小的设置,无法限定缓存体的大小以及存储数据的限制
- 没有弱键引用,在内存占用吃紧的情况下,JVM是无法回收的
阿里笔试题在规定的时间内,用hashMap实现一个缓存工具类
要点:
- 不可变对象
- 单例
- 线程安全
- 回收失效数据
- 垃圾回收
- 缓存大小
- LRU算法
代码如下
/*** Created by zhangshukang on 2018/8/1.*/public class CacheManager {private CacheManager() {}//是否开启清除失效缓存private volatile Boolean clearExpireCacheEnable = true;//缓存失效时间private long cacheTimeout = 12 * 60 * 60 * 1000L;//缓存使用记录private static LinkedList<Object> cacheUseRecord = new LinkedList<>();//可缓存最大数量private static Integer MAX_CACHE_SIZE = 80;//重入读写锁private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();private static Lock writeLock = reentrantReadWriteLock.writeLock();private static Lock readLock = reentrantReadWriteLock.readLock();private final static Map<Object, CacheEntry> cacheEntryMap = new ConcurrentHashMap<>();private void init() {initClearTask();}//自定义缓存失效时间private void init(long cacheTimes) {this.cacheTimeout = cacheTimes;initClearTask();}private void initClearTask() {//启动清除失效缓存数据if (clearExpireCacheEnable) {new ClearCacheTask().start();}}private static CacheManager getCacheManagerInstance() {return CacheManagerFactory.CACHE_MANAGER;}private static class CacheManagerFactory {private static final CacheManager CACHE_MANAGER = new CacheManager();}private class ClearCacheTask extends Thread {ClearCacheTask() {super.setName("clear cache task start ...");}@Overridepublic void run() {while (clearExpireCacheEnable) {try {long now = System.currentTimeMillis();//定时清理try {// Thread.sleep(1000 * 60 * 60);} catch (Exception e) {e.printStackTrace();}cacheEntryMap.keySet().stream().forEach(key -> {try {writeLock.lock();//判断使用记录中的key是否已经被LRU清除if (!cacheUseRecord.contains(key)) {return;}CacheEntry entry = cacheEntryMap.get(key);if (now - entry.lastTouchTime >= cacheTimeout) {cacheEntryMap.remove(key);cacheUseRecord.remove(key);System.out.println("清理缓存key:" + key);}} finally {writeLock.unlock();}});Thread.sleep(cacheTimeout);} catch (Exception e) {e.printStackTrace();}}}}//失效时间,value,entry,可根据需要决定是否继承Map.Entry<K,V>@Dataprivate class CacheEntry {long lastTouchTime;Object value;CacheEntry(Object value) {super();this.value = value;this.lastTouchTime = System.currentTimeMillis();}}public Object get(Object key) {readLock.lock();CacheEntry entry = null;try {entry = cacheEntryMap.get(key);} finally {readLock.unlock();}if (null == entry)return null;//更新缓存访问时间touchCache(entry);//更新使用记录touchUseRecord(key);return entry == null ? null : entry.value;}//更新缓存访问时间public static void touchCache(CacheEntry entry) {writeLock.lock();try {entry.setLastTouchTime(System.currentTimeMillis());} finally {writeLock.unlock();}}//更新缓存使用记录public static void touchUseRecord(Object key) {writeLock.lock();try {//删除使用记录cacheUseRecord.remove(key);//新增使用记录到首位cacheUseRecord.add(0, key);} finally {writeLock.unlock();}}public Object put(Object key, Object value) throws Exception {//判断缓存大小是否够用,否则根据LRU删除最久未使用的元素if (cacheEntryMap.size() > MAX_CACHE_SIZE) {deleteLRU();}if (cacheEntryMap.size() > MAX_CACHE_SIZE) {throw new Exception("缓存大小超出限制");}CacheEntry entry = new CacheEntry(value);writeLock.lock();try {cacheEntryMap.put(key, entry);cacheUseRecord.add(0, key);} finally {writeLock.unlock();}return value;}/*** 删除最近最久未使用的缓存*/public static void deleteLRU() {Object cacheKey = null;writeLock.lock();try {cacheKey = cacheUseRecord.remove(cacheUseRecord.size() - 1);cacheEntryMap.remove(cacheKey);System.out.println("LRU清除元素key:" + cacheKey);} finally {writeLock.unlock();}}public static void delete(Object key) {if (null == key)return;writeLock.lock();try {cacheEntryMap.remove(key);cacheUseRecord.remove(key);} finally {writeLock.unlock();}}public static void clear() {writeLock.lock();try {cacheEntryMap.clear();cacheUseRecord.clear();} finally {writeLock.unlock();}}public static void main(String[] args) throws Exception {CacheManager cacheManager = CacheManager.getCacheManagerInstance();cacheManager.init(0);for (int i = 0; i < 200; i++) {cacheManager.put(i + "", i);}}}
2、JVM缓存更新问题
不允许重启服务情况下
其实方案也很多,可以使用用zookeeper,也可以使用分布式配置,这里是比较推荐使用分布式配置中心的,可以将这些数据配置到分布式配置中心去!
