java中怎么做缓存_Java实现一个简单的缓存方法
缓存是在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 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 getAllKeys();
}
实现接口ICacheManager,CacheManagerImpl.java
这里我使用了ConcurrentHashMap来保存缓存,本来以为这样就是线程安全的,其实不然,在后面的测试中会发现它并不是线程安全的。
public class CacheManagerImpl implements ICacheManager {
private static Map caches = new ConcurrentHashMap();
/**
* 存入缓存
* @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 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 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块也行,都是大同小异。更高效的方法我暂时也想不出来,希望大家能多多指教。
java中怎么做缓存_Java实现一个简单的缓存方法相关推荐
- java 缓存方法_Java实现一个简单的缓存方法
Java实现一个简单的缓存方法 发布时间:2020-09-07 21:39:55 来源:脚本之家 阅读:99 作者:BrightLoong 缓存是在web开发中经常用到的,将程序经常使用到或调用到的对 ...
- java中如何读写文件_JAVA: 读写文件的几种方法
如果您使用java8,可以也参考这篇文章:JAVA: Java8流逐行读取文件 import java.io.BufferedReader; import java.io.BufferedWriter ...
- java中数据类型的等级_Java 数据类型、变量
Java 数据类型 在 Java 中,对于每一种数据都定义了明确的具体的数据类型,在内存中分配了不同大小的内存空间. 整数类型 (byte.short.int.long) 1.Java 各整数类型有固 ...
- 【Java基础】Java中的char是否可以存储一个中文字符之理解字符字节以及编码集
Java中的一个char采用的是Unicode编码集,占用两个字节,而一个中文字符也是两个字节,因此Java中的char是可以表示一个中文字符的. 但是在C/C++中由于采用的字符编码集是ASCII, ...
- JAVA中string的赋值_Java中关于String赋值的两种形式
说明: String在java中是使用频率很高的一个类,之前使用的时候并没有过多的在意,只知道有两种赋值方式,一种是采用字面值方式(String string1 = "string" ...
- java中如何实现继承_Java继承
Java继承 1.什么是继承呢? 类似人类中的继承(继承上一辈.上上辈的东西),在Java中就是将一个已知的类进行拓展,让一个新的类来继承这个已知的类,这样就能使这个新的类具有已知类的属性和行为. 新 ...
- extends在java中是什么意思_java中extends的意思是什么
java中extends的意思是什么 发布时间:2020-07-29 10:35:12 来源:亿速云 阅读:79 作者:Leah 今天就跟大家聊聊有关java中extends的意思是什么,可能很多人都 ...
- java中给对象的List集合去重的几种方法(Lambda)
java中给对象的List集合去重的几种方法 前言 一.lambda表达式的去重方式 二.Stream API中的collect去重方法 三.Stream API 中的distinct方法去重 前言 ...
- 电脑用java怎么编程,使用java编程从0到1实现一个简单计算器
学习编程语言最重要的就是实践.很多小伙伴在学习完编程语言后,一直停留在基础阶段,只懂一大堆理论知识,而不懂得实践.那么,今天我们一起来动手做一个小计算器,回顾下学习过的知识,同时这也是很多大学计算机专 ...
- Java 中的 断言 (assert)—— 初识 简单的使用和理解~只要这一篇就够了
首先老样子,三个方面来理解 断言是个啥(尝试初步<理解>) 如何使用(到底咋子个用,啥时候用) 个人的思考理解和总结(说话不负责时间) 断言与if的区别 断言和异常的区别 总结 新年快乐! ...
最新文章
- 字符串分割split
- 如何使用TensorRT对训练好的PyTorch模型进行加速?
- [LeetCode]--290. Word Pattern
- 菜鸟的 Sass 学习笔记
- 自由自在意式手工冰淇淋 果香浪漫的甜蜜
- 关于报工和生产订单的一些状态解释
- Python 框架 之 Scrapy 爬虫(二)
- CSDN2019博客之星评选——期待各位大佬的投票!
- C++11/14::右值引用
- 水经注全球离线地图数据3.0即将发布
- 3次样条曲线差值函数c++实现
- catia 快捷键 激活零件_CATIA教程:CATIA快捷键及学习技巧总结
- plcst语言编程教程_st语言编程实例讲解
- 魔兽争霸如何修改快捷键
- Android知识图谱(持续更新中)
- oracle中cube的作用,Oraclerollup和cube分析
- POWER BI:SSAS表格建模(转自博客园,ps:部分已修改)
- 软碟通 (UltraISO)制作启动盘
- easyx的使用(1)
- 自己的电脑不能连接打印机打印怎么办
热门文章
- 自制全铝CNC雕刻机全过程(完工篇)
- 【微分方程数值解】常\偏微分方程及其常用数值解法概述
- 获取网络时间(国家标准时间)
- mysql英文怎么发音_英文字母和汉语拼音先学哪个?科学答案在这里
- c语言背包问题非递归算法,数据结构基础 背包问题(一) 之 非递归解
- commons-pool.jar 和commons-dbcp.jar 下载地址
- [推荐] [推荐] 黑苹果驱动之家
- [3]、Swagger——解决前后端打架问题
- clockworkmod CWM简单介绍
- 游戏策划案应该分哪几个方面来编写?制作游戏系统应该准备整理那些方面的内容?游戏策划相关分析