栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

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

创建缓存对象EntityCache.java

public class EntityCache {
  
  private Object datas;

  
  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 {
  
  void putCache(String key, EntityCache cache);

  
  void putCache(String key, Object datas, long timeOut);

  
  EntityCache getCacheByKey(String key);

  
  Object getCacheDataByKey(String key);

  
  Map getCacheAll();

  
  boolean isContains(String key);

  
  void clearAll();

  
  void clearByKey(String key);

  
  boolean isTimeOut(String key);

  
  Set getAllKeys();
}

实现接口ICacheManager,CacheManagerImpl.java

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

public class CacheManagerImpl implements ICacheManager {
  private static Map caches = new ConcurrentHashMap();

  
  public void putCache(String key, EntityCache cache) {
    caches.put(key, cache);
  }

  
  public void putCache(String key, Object datas, long timeOut) {
    timeOut = timeOut > 0 ? timeOut : 0L;
    putCache(key, new EntityCache(datas, timeOut, System.currentTimeMillis()));
  }

  
  public EntityCache getCacheByKey(String key) {
    if (this.isContains(key)) {
      return caches.get(key);
    }
    return null;
  }

  
  public Object getCacheDataByKey(String key) {
    if (this.isContains(key)) {
      return caches.get(key).getDatas();
    }
    return null;
  }

  
  public Map getCacheAll() {
    return caches;
  }

  
  public boolean isContains(String key) {
    return caches.containsKey(key);
  }

  
  public void clearAll() {
    caches.clear();
  }

  
  public void clearByKey(String key) {
    if (this.isContains(key)) {
      caches.remove(key);
    }
  }

  
  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;
  }

  
  public Set 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:test
2017-4-17 10:33:51 io.github.brightloong.cache.TestCache testCacheManager
信息: myTest:myTest
2017-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:null
2017-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块也行,都是大同小异。更高效的方法我暂时也想不出来,希望大家能多多指教。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/146707.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号