缓存工具CacheUtil - 并发环境的缓存值存取
缓存工具CacheUtil - 并发环境的缓存值存取
目的
- 适合并发环境的缓存值存取
- 读取缓存值时,只需关注数据来源。不用再关注将源数据存入缓存等后续处理。
- 应用程序N次读取数据时,数据源读取一次,缓存读取N-1次。
设计
当从缓存查找失败,则去数据源获取。获取成功,存入缓存并返回。
实现
Sourcable
/**
* 可溯源的。可从数据源获取数据
*/
public interface Sourcable {
/**
* 从数据源获取
*/
<T> T get();
}
CacheUtil
public class CacheUtil {
private static final transient Log logger = LogFactory.getLog(CacheUtil.class); // @Resource(name = "memCacheServiceImpl")
private static CacheService cache; /**
* 获取value
* 如果从缓存查找失败,则尝试从数据源获取,获取成功,存入缓存并返回。
* @param key
* @param source Sourcable的实现,用于读取源数据。
* @param isShort 是否(短时间)暂存
*/
public static <T> T get(String key, Sourcable source, boolean isShort) {
logger.debug("-> [" + Thread.currentThread().getId() + "] Enter");
T value = get(key);
if (value == null && source != null) {
// 缓存查找失败,尝试从数据源获取
logger.debug("-> [" + Thread.currentThread().getId() + "] try to Lock");
key = key.intern();
synchronized (key) {
logger.debug("-> [" + Thread.currentThread().getId() + "] Locked");
value = get(key);
// 双重检查。防止多线程重复从数据源读取
if (value == null) {
// 从数据源获取
value = source.get();
if (value != null) {
// 存入缓存
if (isShort) {
// 暂存cache
put4short(key, value);
} else {
// 常规存入cache,正常有效期
put(key, value);
}
}
logger.debug("-> [" + Thread.currentThread().getId() + "] Loaded");
}
}
}
logger.debug("-> [" + Thread.currentThread().getId() + "] get: " + value);
return value;
} // 其他代码省略.. }
测试
- CacheUtilTest
public class CacheUtilTest extends SpringTest { @Test
public void get() throws Exception {
final Long resourceId = 173L;
final String cacheKey = Resource.class.getName() + '#' + resourceId; // 清除
CacheUtil.delete(cacheKey); // 1000个线程并发存取
int num = 1000;
Executor executor = Executors.newFixedThreadPool(num);
final CountDownLatch latch = new CountDownLatch(num);
for (int i = 0; i < num; i++) {
executor.execute(() -> {
// new String(cacheKey)。 用于验证不同key对象时,同步锁的有效性
String key = new String(cacheKey);
// 读取缓存值时,只需关注数据来源。不用再关注将源数据存入缓存。
Resource resource = CacheUtil.get(key, new Sourcable() {
@Override
public <T> T get() {
List<Resource> resources = DAOUtil.findByHql("FROM Resource WHERE resourceId=" + resourceId);
if (CollectionUtils.isNotEmpty(resources)) {
return resources.get(0);
}
return null;
}
});
latch.countDown();
});
}
latch.await();
}
}
附录
SpringTest
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:context/spring.xml")
public abstract class SpringTest {}
结果
10个线程运行结果:
[PAY][DEBUG] 2016-10-14 15:14:20,526 (CacheUtil.java:46).get - [pool-1-thread-2] 14030
-> [29] Enter
[PAY][DEBUG] 2016-10-14 15:14:20,527 (CacheUtil.java:46).get - [pool-1-thread-8] 14031
-> [35] Enter
[PAY][DEBUG] 2016-10-14 15:14:20,527 (CacheUtil.java:46).get - [pool-1-thread-10] 14031
-> [37] Enter
[PAY][DEBUG] 2016-10-14 15:14:20,528 (CacheUtil.java:46).get - [pool-1-thread-9] 14032
-> [36] Enter
[PAY][DEBUG] 2016-10-14 15:14:20,527 (CacheUtil.java:46).get - [pool-1-thread-4] 14031
-> [31] Enter
[PAY][DEBUG] 2016-10-14 15:14:20,527 (CacheUtil.java:46).get - [pool-1-thread-7] 14031
-> [34] Enter
[PAY][DEBUG] 2016-10-14 15:14:20,526 (CacheUtil.java:46).get - [pool-1-thread-6] 14030
-> [33] Enter
[PAY][DEBUG] 2016-10-14 15:14:20,526 (CacheUtil.java:46).get - [pool-1-thread-5] 14030
-> [32] Enter
[PAY][DEBUG] 2016-10-14 15:14:20,555 (CacheUtil.java:49).get - [pool-1-thread-7] 14059
-> [34] try to Lock
[PAY][DEBUG] 2016-10-14 15:14:20,557 (CacheUtil.java:53).get - [pool-1-thread-7] 14061
-> [34] Locked
[PAY][DEBUG] 2016-10-14 15:14:20,561 (CacheUtil.java:49).get - [pool-1-thread-5] 14065
-> [32] try to Lock
[PAY][DEBUG] 2016-10-14 15:14:20,555 (CacheUtil.java:49).get - [pool-1-thread-4] 14059
-> [31] try to Lock
[PAY][DEBUG] 2016-10-14 15:14:20,547 (CacheUtil.java:49).get - [pool-1-thread-10] 14051
-> [37] try to Lock
[PAY][DEBUG] 2016-10-14 15:14:20,547 (CacheUtil.java:49).get - [pool-1-thread-8] 14051
-> [35] try to Lock
[PAY][DEBUG] 2016-10-14 15:14:20,547 (CacheUtil.java:49).get - [pool-1-thread-9] 14051
-> [36] try to Lock
[PAY][DEBUG] 2016-10-14 15:14:20,547 (CacheUtil.java:49).get - [pool-1-thread-2] 14051
-> [29] try to Lock
[PAY][DEBUG] 2016-10-14 15:14:20,528 (CacheUtil.java:46).get - [pool-1-thread-1] 14032
-> [28] Enter
[PAY][DEBUG] 2016-10-14 15:14:20,527 (CacheUtil.java:46).get - [pool-1-thread-3] 14031
-> [30] Enter
[PAY][DEBUG] 2016-10-14 15:14:20,565 (CacheUtil.java:49).get - [pool-1-thread-6] 14069
-> [33] try to Lock
Hibernate: FROM Resource WHERE resourceId=173
[PAY][DEBUG] 2016-10-14 15:14:20,595 (CacheUtil.java:49).get - [pool-1-thread-1] 14099
-> [28] try to Lock
[PAY][DEBUG] 2016-10-14 15:14:20,599 (CacheUtil.java:49).get - [pool-1-thread-3] 14103
-> [30] try to Lock
[PAY][DEBUG] 2016-10-14 15:14:20,639 (CacheUtil.java:72).get - [pool-1-thread-7] 14143
-> [34] Loaded
[PAY][DEBUG] 2016-10-14 15:14:20,639 (CacheUtil.java:53).get - [pool-1-thread-3] 14143
-> [30] Locked
[PAY][DEBUG] 2016-10-14 15:14:20,639 (CacheUtil.java:76).get - [pool-1-thread-7] 14143
-> [34] get: /uploadfiles/resource/2014122313575119.png
[PAY][DEBUG] 2016-10-14 15:14:20,645 (CacheUtil.java:53).get - [pool-1-thread-1] 14149
-> [28] Locked
[PAY][DEBUG] 2016-10-14 15:14:20,645 (CacheUtil.java:76).get - [pool-1-thread-3] 14149
-> [30] get: /uploadfiles/resource/2014122313575119.png
[PAY][DEBUG] 2016-10-14 15:14:20,651 (CacheUtil.java:76).get - [pool-1-thread-1] 14155
-> [28] get: /uploadfiles/resource/2014122313575119.png
[PAY][DEBUG] 2016-10-14 15:14:20,651 (CacheUtil.java:53).get - [pool-1-thread-6] 14155
-> [33] Locked
[PAY][DEBUG] 2016-10-14 15:14:20,658 (CacheUtil.java:76).get - [pool-1-thread-6] 14162
-> [33] get: /uploadfiles/resource/2014122313575119.png
[PAY][DEBUG] 2016-10-14 15:14:20,658 (CacheUtil.java:53).get - [pool-1-thread-2] 14162
-> [29] Locked
[PAY][DEBUG] 2016-10-14 15:14:20,665 (CacheUtil.java:76).get - [pool-1-thread-2] 14169
-> [29] get: /uploadfiles/resource/2014122313575119.png
[PAY][DEBUG] 2016-10-14 15:14:20,665 (CacheUtil.java:53).get - [pool-1-thread-9] 14169
-> [36] Locked
[PAY][DEBUG] 2016-10-14 15:14:20,671 (CacheUtil.java:76).get - [pool-1-thread-9] 14175
-> [36] get: /uploadfiles/resource/2014122313575119.png
[PAY][DEBUG] 2016-10-14 15:14:20,671 (CacheUtil.java:53).get - [pool-1-thread-8] 14175
-> [35] Locked
[PAY][DEBUG] 2016-10-14 15:14:20,676 (CacheUtil.java:76).get - [pool-1-thread-8] 14180
-> [35] get: /uploadfiles/resource/2014122313575119.png
[PAY][DEBUG] 2016-10-14 15:14:20,676 (CacheUtil.java:53).get - [pool-1-thread-10] 14180
-> [37] Locked
[PAY][DEBUG] 2016-10-14 15:14:20,681 (CacheUtil.java:76).get - [pool-1-thread-10] 14185
-> [37] get: /uploadfiles/resource/2014122313575119.png
[PAY][DEBUG] 2016-10-14 15:14:20,681 (CacheUtil.java:53).get - [pool-1-thread-4] 14185
-> [31] Locked
[PAY][DEBUG] 2016-10-14 15:14:20,686 (CacheUtil.java:76).get - [pool-1-thread-4] 14190
-> [31] get: /uploadfiles/resource/2014122313575119.png
[PAY][DEBUG] 2016-10-14 15:14:20,686 (CacheUtil.java:53).get - [pool-1-thread-5] 14190
-> [32] Locked
[PAY][DEBUG] 2016-10-14 15:14:20,694 (CacheUtil.java:76).get - [pool-1-thread-5] 14198
-> [32] get: /uploadfiles/resource/2014122313575119.png
只有一个id为34的线程Loaded,其余线程均等待 线程34 处理(从数据库查询结果并存入缓存)完成后,再查找缓存从而命中数据。
问题
- 数据源失效问题
当数据源不存在数据时,一直缓存失败,则会一直从数据源获取数据。从而造成数据源负担。
为防止这个情况,可以再进一步做个过滤保障机制。
缓存工具CacheUtil - 并发环境的缓存值存取的更多相关文章
- Cache【硬盘缓存工具类(包含内存缓存LruCache和磁盘缓存DiskLruCache)】
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 内存缓存LruCache和磁盘缓存DiskLruCache的封装类,主要用于图片缓存. 效果图 代码分析 内存缓存LruCache和 ...
- 如何在高并发环境下设计出无锁的数据库操作(Java版本)
一个在线2k的游戏,每秒钟并发都吓死人.传统的hibernate直接插库基本上是不可行的.我就一步步推导出一个无锁的数据库操作. 1. 并发中如何无锁. 一个很简单的思路,把并发转化成为单线程.Jav ...
- 【高并发】高并发环境下构建缓存服务需要注意哪些问题?我和阿里P9聊了很久!
写在前面 周末,跟阿里的一个朋友(去年晋升为P9了)聊了很久,聊的内容几乎全是技术,当然了,两个技术男聊得最多的话题当然就是技术了.从基础到架构,从算法到AI,无所不谈.中间又穿插着不少天马行空的想象 ...
- Java 使用Redis缓存工具的图文详细方法
开始在 Java 中使用 Redis 前, 我们需要确保已经安装了 redis 服务及 Java redis 驱动,且你的机器上能正常使用 Java. (1)Java的安装配置可以参考我们的 Java ...
- Go/Python/Erlang编程语言对比分析及示例 基于RabbitMQ.Client组件实现RabbitMQ可复用的 ConnectionPool(连接池) 封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil 分享基于MemoryCache(内存缓存)的缓存工具类,C# B/S 、C/S项目均可以使用!
Go/Python/Erlang编程语言对比分析及示例 本文主要是介绍Go,从语言对比分析的角度切入.之所以选择与Python.Erlang对比,是因为做为高级语言,它们语言特性上有较大的相似性, ...
- 从缓存入门到并发编程三要素详解 Java中 volatile 、final 等关键字解析案例
引入高速缓存概念 在计算机在执行程序时,以指令为单位来执行,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入. 由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这 ...
- 三分钟学会缓存工具DiskLruCache
DiskLruCache是一个十分好用的android缓存工具,我们可以从GitHub上下载其源码:https://github.com/JakeWharton/DiskLruCache DiskLr ...
- Java高并发--CPU多级缓存与Java内存模型
Java高并发--CPU多级缓存与Java内存模型 主要是学习慕课网实战视频<Java并发编程入门与高并发面试>的笔记 CPU多级缓存 为什么需要CPU缓存:CPU的频率太快,以至于主存跟 ...
- No caching ——无缓存工具
No caching ——无缓存工具 无缓存工具阻止客户端应用程序(如Web浏览器)缓存任何资源,因此,请求总是发送到远程站点,所以我们总能看到最新版本. 适用场景 开发每次新部署了一版环境,说解决了 ...
随机推荐
- powerdesigner导出word
1.网上下载word模板,或者是自制模板 2.报告->导出,选择刚才下载的模板,导出即可
- 右键添加 CMD 命令提示符
# 右键添加 CMD 命令提示符 当然是修改注册表 - 打开注册表编辑器(按下Win+R打开运行对话框,输入regedit),找到[HKEY_CLASSES_ROOT/Folder/shell] ...
- python---dnspython
dnspython 是Python实现的一个DNS工具包,支持几乎所有的记录类型,可以用于查询,传输并动态更新ZONE信息,同时支持TSIG(事务签名)验证消息和EDNS0(扩展DNS).可以替代ns ...
- oracle 11g dbf数据文件从C盘迁移到D盘
服务器系统为 windows 2008 R2 64位,由于C盘空间将满,要将C盘的oracle的DBF数据文件迁移到D盘下,步骤如下: 1.输入cmd,启动 cmd.exe窗口 2.输入 sqlplu ...
- 在.htaccess文件中写RewriteRule无效的问题的解决
近来在Apache Rewrite 拟静态配置时,遇到个问题.写的如下: RewriteEngine onRewriteRule ^/t_(.*)/$ /test.php?id=$1 保存在httpd ...
- Spring MVC实现Junit Case
Spring MVC中编写单元测试(WEB项目): 1. 首先开发一个基类,用于载入配置文件.以下所有的测试实现类都要继承这个类 package com.yusj.basecase; import o ...
- 【HOW】如何对Reporting Services表格中数据按字段排序
Reporting Services中可以设置排序的地方非常多,有很多地方从其字面意思上好像是对表格数据的排序,但实际都不管用.在多次尝试后,得到如下的有效设置方式: 1. 鼠标右键单击要排序字段的内 ...
- 仅个人兴趣,自己通过搜索他人的成果,结合自己的理解,来分析discuz的代码。
仅个人兴趣,自己通过搜索他人的成果,结合自己的理解,来分析discuz的代码. discuz 版本: 3.2
- 黑马程序员_ Objective-c 之Foundation笔记(一)
结构体 NSRange: 用来表示范围 创建 NSRange r1 = {2, 4} NSRange r2 = {.location = 2, .length = 4} NSRange r3 = N ...
- 从一个int值显示相应枚举类型的名称或者描述
我正在做一个出入库管理的简单项目,在Models里定义了这样的枚举类型 public enum InOrOut { [Description("出库")] Out = , [Des ...