1、本地jvm缓存

缓存是将数据在整个程序生命周期中存储的内存
作用:

  • 能够提升获取数据的效率,避免IO读取耗时操作。
  • 被大量的读取,更新频率低。
  • 经常变化,但是不会被持久化的。

方式:
使用static关键字开辟内存与搜索时间复杂度O(1)的hashmap来存储数据提高读取效率;然后实现相对应的缓存写入,更新,读取,删除等逻辑

spring-boot提供了一个spring-boot-devtools包,其作用就是在不重启服务的情况下将服务重新部署到服务器上。原理是使用两个类加载器,一个用于加载变化的类文件,一个用于加载未发生变化的类文件,直接对未改变的类文件进行加载极大的提高整个效率。
或者使用Redis作为缓存。

HashMap缺点

  1. 没有缓存大小的设置,无法限定缓存体的大小以及存储数据的限制
  2. 没有弱键引用,在内存占用吃紧的情况下,JVM是无法回收的

阿里笔试题在规定的时间内,用hashMap实现一个缓存工具类

要点:

  • 不可变对象
  • 单例
  • 线程安全
  • 回收失效数据
  • 垃圾回收
  • 缓存大小
  • LRU算法
    代码如下
  1. /**
  2. * Created by zhangshukang on 2018/8/1.
  3. */
  4. public class CacheManager {
  5. private CacheManager() {
  6. }
  7. //是否开启清除失效缓存
  8. private volatile Boolean clearExpireCacheEnable = true;
  9. //缓存失效时间
  10. private long cacheTimeout = 12 * 60 * 60 * 1000L;
  11. //缓存使用记录
  12. private static LinkedList<Object> cacheUseRecord = new LinkedList<>();
  13. //可缓存最大数量
  14. private static Integer MAX_CACHE_SIZE = 80;
  15. //重入读写锁
  16. private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
  17. private static Lock writeLock = reentrantReadWriteLock.writeLock();
  18. private static Lock readLock = reentrantReadWriteLock.readLock();
  19. private final static Map<Object, CacheEntry> cacheEntryMap = new ConcurrentHashMap<>();
  20. private void init() {
  21. initClearTask();
  22. }
  23. //自定义缓存失效时间
  24. private void init(long cacheTimes) {
  25. this.cacheTimeout = cacheTimes;
  26. initClearTask();
  27. }
  28. private void initClearTask() {
  29. //启动清除失效缓存数据
  30. if (clearExpireCacheEnable) {
  31. new ClearCacheTask().start();
  32. }
  33. }
  34. private static CacheManager getCacheManagerInstance() {
  35. return CacheManagerFactory.CACHE_MANAGER;
  36. }
  37. private static class CacheManagerFactory {
  38. private static final CacheManager CACHE_MANAGER = new CacheManager();
  39. }
  40. private class ClearCacheTask extends Thread {
  41. ClearCacheTask() {
  42. super.setName("clear cache task start ...");
  43. }
  44. @Override
  45. public void run() {
  46. while (clearExpireCacheEnable) {
  47. try {
  48. long now = System.currentTimeMillis();
  49. //定时清理
  50. try {
  51. // Thread.sleep(1000 * 60 * 60);
  52. } catch (Exception e) {
  53. e.printStackTrace();
  54. }
  55. cacheEntryMap.keySet().stream().forEach(key -> {
  56. try {
  57. writeLock.lock();
  58. //判断使用记录中的key是否已经被LRU清除
  59. if (!cacheUseRecord.contains(key)) {
  60. return;
  61. }
  62. CacheEntry entry = cacheEntryMap.get(key);
  63. if (now - entry.lastTouchTime >= cacheTimeout) {
  64. cacheEntryMap.remove(key);
  65. cacheUseRecord.remove(key);
  66. System.out.println("清理缓存key:" + key);
  67. }
  68. } finally {
  69. writeLock.unlock();
  70. }
  71. });
  72. Thread.sleep(cacheTimeout);
  73. } catch (Exception e) {
  74. e.printStackTrace();
  75. }
  76. }
  77. }
  78. }
  79. //失效时间,value,entry,可根据需要决定是否继承Map.Entry<K,V>
  80. @Data
  81. private class CacheEntry {
  82. long lastTouchTime;
  83. Object value;
  84. CacheEntry(Object value) {
  85. super();
  86. this.value = value;
  87. this.lastTouchTime = System.currentTimeMillis();
  88. }
  89. }
  90. public Object get(Object key) {
  91. readLock.lock();
  92. CacheEntry entry = null;
  93. try {
  94. entry = cacheEntryMap.get(key);
  95. } finally {
  96. readLock.unlock();
  97. }
  98. if (null == entry)
  99. return null;
  100. //更新缓存访问时间
  101. touchCache(entry);
  102. //更新使用记录
  103. touchUseRecord(key);
  104. return entry == null ? null : entry.value;
  105. }
  106. //更新缓存访问时间
  107. public static void touchCache(CacheEntry entry) {
  108. writeLock.lock();
  109. try {
  110. entry.setLastTouchTime(System.currentTimeMillis());
  111. } finally {
  112. writeLock.unlock();
  113. }
  114. }
  115. //更新缓存使用记录
  116. public static void touchUseRecord(Object key) {
  117. writeLock.lock();
  118. try {
  119. //删除使用记录
  120. cacheUseRecord.remove(key);
  121. //新增使用记录到首位
  122. cacheUseRecord.add(0, key);
  123. } finally {
  124. writeLock.unlock();
  125. }
  126. }
  127. public Object put(Object key, Object value) throws Exception {
  128. //判断缓存大小是否够用,否则根据LRU删除最久未使用的元素
  129. if (cacheEntryMap.size() > MAX_CACHE_SIZE) {
  130. deleteLRU();
  131. }
  132. if (cacheEntryMap.size() > MAX_CACHE_SIZE) {
  133. throw new Exception("缓存大小超出限制");
  134. }
  135. CacheEntry entry = new CacheEntry(value);
  136. writeLock.lock();
  137. try {
  138. cacheEntryMap.put(key, entry);
  139. cacheUseRecord.add(0, key);
  140. } finally {
  141. writeLock.unlock();
  142. }
  143. return value;
  144. }
  145. /**
  146. * 删除最近最久未使用的缓存
  147. */
  148. public static void deleteLRU() {
  149. Object cacheKey = null;
  150. writeLock.lock();
  151. try {
  152. cacheKey = cacheUseRecord.remove(cacheUseRecord.size() - 1);
  153. cacheEntryMap.remove(cacheKey);
  154. System.out.println("LRU清除元素key:" + cacheKey);
  155. } finally {
  156. writeLock.unlock();
  157. }
  158. }
  159. public static void delete(Object key) {
  160. if (null == key)
  161. return;
  162. writeLock.lock();
  163. try {
  164. cacheEntryMap.remove(key);
  165. cacheUseRecord.remove(key);
  166. } finally {
  167. writeLock.unlock();
  168. }
  169. }
  170. public static void clear() {
  171. writeLock.lock();
  172. try {
  173. cacheEntryMap.clear();
  174. cacheUseRecord.clear();
  175. } finally {
  176. writeLock.unlock();
  177. }
  178. }
  179. public static void main(String[] args) throws Exception {
  180. CacheManager cacheManager = CacheManager.getCacheManagerInstance();
  181. cacheManager.init(0);
  182. for (int i = 0; i < 200; i++) {
  183. cacheManager.put(i + "", i);
  184. }
  185. }
  186. }

2、JVM缓存更新问题

不允许重启服务情况下
其实方案也很多,可以使用用zookeeper,也可以使用分布式配置,这里是比较推荐使用分布式配置中心的,可以将这些数据配置到分布式配置中心去!