首页 > 编程 > Java > 正文

Java实现一个简单的缓存方法

2019-11-26 12:33:53
字体:
来源:转载
供稿:网友

缓存是在web开发中经常用到的,将程序经常使用到或调用到的对象存在内存中,或者是耗时较长但又不具有实时性的查询数据放入内存中,在一定程度上可以提高性能和效率。下面我实现了一个简单的缓存,步骤如下。

创建缓存对象EntityCache.java

public class EntityCache {  /**   * 保存的数据   */  private Object datas;  /**   * 设置数据失效时间,为0表示永不失效   */  private long timeOut;  /**   * 最后刷新时间   */  private long lastRefeshTime;  public EntityCache(Object datas, long timeOut, long lastRefeshTime) {    this.datas = datas;    this.timeOut = timeOut;    this.lastRefeshTime = lastRefeshTime;  }  public Object getDatas() {    return datas;  }  public void setDatas(Object datas) {    this.datas = datas;  }  public long getTimeOut() {    return timeOut;  }  public void setTimeOut(long timeOut) {    this.timeOut = timeOut;  }  public long getLastRefeshTime() {    return lastRefeshTime;  }  public void setLastRefeshTime(long lastRefeshTime) {    this.lastRefeshTime = lastRefeshTime;  }}

定义缓存操作接口,ICacheManager.java

public interface ICacheManager {  /**   * 存入缓存   * @param key   * @param cache   */  void putCache(String key, EntityCache cache);  /**   * 存入缓存   * @param key   * @param cache   */  void putCache(String key, Object datas, long timeOut);  /**   * 获取对应缓存   * @param key   * @return   */  EntityCache getCacheByKey(String key);  /**   * 获取对应缓存   * @param key   * @return   */  Object getCacheDataByKey(String key);  /**   * 获取所有缓存   * @param key   * @return   */  Map<String, EntityCache> getCacheAll();  /**   * 判断是否在缓存中   * @param key   * @return   */  boolean isContains(String key);  /**   * 清除所有缓存   */  void clearAll();  /**   * 清除对应缓存   * @param key   */  void clearByKey(String key);  /**   * 缓存是否超时失效   * @param key   * @return   */  boolean isTimeOut(String key);  /**   * 获取所有key   * @return   */  Set<String> getAllKeys();}

实现接口ICacheManager,CacheManagerImpl.java

这里我使用了ConcurrentHashMap来保存缓存,本来以为这样就是线程安全的,其实不然,在后面的测试中会发现它并不是线程安全的。

public class CacheManagerImpl implements ICacheManager {  private static Map<String, EntityCache> caches = new ConcurrentHashMap<String, EntityCache>();  /**   * 存入缓存   * @param key   * @param cache   */  public void putCache(String key, EntityCache cache) {    caches.put(key, cache);  }  /**   * 存入缓存   * @param key   * @param cache   */  public void putCache(String key, Object datas, long timeOut) {    timeOut = timeOut > 0 ? timeOut : 0L;    putCache(key, new EntityCache(datas, timeOut, System.currentTimeMillis()));  }  /**   * 获取对应缓存   * @param key   * @return   */  public EntityCache getCacheByKey(String key) {    if (this.isContains(key)) {      return caches.get(key);    }    return null;  }  /**   * 获取对应缓存   * @param key   * @return   */  public Object getCacheDataByKey(String key) {    if (this.isContains(key)) {      return caches.get(key).getDatas();    }    return null;  }  /**   * 获取所有缓存   * @param key   * @return   */  public Map<String, EntityCache> getCacheAll() {    return caches;  }  /**   * 判断是否在缓存中   * @param key   * @return   */  public boolean isContains(String key) {    return caches.containsKey(key);  }  /**   * 清除所有缓存   */  public void clearAll() {    caches.clear();  }  /**   * 清除对应缓存   * @param key   */  public void clearByKey(String key) {    if (this.isContains(key)) {      caches.remove(key);    }  }  /**   * 缓存是否超时失效   * @param key   * @return   */  public boolean isTimeOut(String key) {    if (!caches.containsKey(key)) {      return true;    }    EntityCache cache = caches.get(key);    long timeOut = cache.getTimeOut();    long lastRefreshTime = cache.getLastRefeshTime();    if (timeOut == 0 || System.currentTimeMillis() - lastRefreshTime >= timeOut) {      return true;    }    return false;  }  /**   * 获取所有key   * @return   */  public Set<String> getAllKeys() {    return caches.keySet();  }}

CacheListener.java,监听失效数据并移除。

public class CacheListener{  Logger logger = Logger.getLogger("cacheLog");  private CacheManagerImpl cacheManagerImpl;  public CacheListener(CacheManagerImpl cacheManagerImpl) {    this.cacheManagerImpl = cacheManagerImpl;  }  public void startListen() {    new Thread(){      public void run() {        while (true) {          for(String key : cacheManagerImpl.getAllKeys()) {            if (cacheManagerImpl.isTimeOut(key)) {             cacheManagerImpl.clearByKey(key);             logger.info(key + "缓存被清除");           }          }         }      }     }.start();  }}

测试类TestCache.java

public class TestCache {  Logger logger = Logger.getLogger("cacheLog");  /**   * 测试缓存和缓存失效   */  @Test  public void testCacheManager() {    CacheManagerImpl cacheManagerImpl = new CacheManagerImpl();    cacheManagerImpl.putCache("test", "test", 10 * 1000L);    cacheManagerImpl.putCache("myTest", "myTest", 15 * 1000L);    CacheListener cacheListener = new CacheListener(cacheManagerImpl);    cacheListener.startListen();    logger.info("test:" + cacheManagerImpl.getCacheByKey("test").getDatas());    logger.info("myTest:" + cacheManagerImpl.getCacheByKey("myTest").getDatas());    try {      TimeUnit.SECONDS.sleep(20);    } catch (InterruptedException e) {      e.printStackTrace();    }    logger.info("test:" + cacheManagerImpl.getCacheByKey("test"));    logger.info("myTest:" + cacheManagerImpl.getCacheByKey("myTest"));  }  /**   * 测试线程安全   */  @Test  public void testThredSafe() {    final String key = "thread";    final CacheManagerImpl cacheManagerImpl = new CacheManagerImpl();    ExecutorService exec = Executors.newCachedThreadPool();    for (int i = 0; i < 100; i++) {      exec.execute(new Runnable() {        public void run() {            if (!cacheManagerImpl.isContains(key)) {              cacheManagerImpl.putCache(key, 1, 0);            } else {              //因为+1和赋值操作不是原子性的,所以把它用synchronize块包起来              synchronized (cacheManagerImpl) {                int value = (Integer) cacheManagerImpl.getCacheDataByKey(key) + 1;                 cacheManagerImpl.putCache(key,value , 0);              }            }        }      });    }    exec.shutdown();     try {      exec.awaitTermination(1, TimeUnit.DAYS);    } catch (InterruptedException e1) {      e1.printStackTrace();    }     logger.info(cacheManagerImpl.getCacheDataByKey(key).toString());  }}

testCacheManager()输出结果如下:

2017-4-17 10:33:51 io.github.brightloong.cache.TestCache testCacheManager信息: test:test2017-4-17 10:33:51 io.github.brightloong.cache.TestCache testCacheManager信息: myTest:myTest2017-4-17 10:34:01 io.github.brightloong.cache.CacheListener$1 run信息: test缓存被清除2017-4-17 10:34:06 io.github.brightloong.cache.CacheListener$1 run信息: myTest缓存被清除2017-4-17 10:34:11 io.github.brightloong.cache.TestCache testCacheManager信息: test:null2017-4-17 10:34:11 io.github.brightloong.cache.TestCache testCacheManager信息: myTest:null

testThredSafe()输出结果如下(选出了各种结果中的一个举例):

2017-4-17 10:35:36 io.github.brightloong.cache.TestCache testThredSafe信息: 96

可以看到并不是预期的结果100,为什么呢?ConcurrentHashMap只能保证单次操作的原子性,但是当复合使用的时候,没办法保证复合操作的原子性,以下代码:

if (!cacheManagerImpl.isContains(key)) {              cacheManagerImpl.putCache(key, 1, 0);            }

多线程的时候回重复更新value,设置为1,所以出现结果不是预期的100。所以办法就是在CacheManagerImpl.java中都加上synchronized,但是这样一来相当于操作都是串行,使用ConcurrentHashMap也没有什么意义,不过只是简单的缓存还是可以的。或者对测试方法中的run里面加上synchronized块也行,都是大同小异。更高效的方法我暂时也想不出来,希望大家能多多指教。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表