`
soongbo
  • 浏览: 88676 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

高速缓存实现

    博客分类:
  • Java
阅读更多
   各位大虾,本人实现了一个高速缓存,实现方式中依赖java的concurrent包ConcurrendHashMap,贴出代码希望各位能够讨论一下如下的addElement()方法不加锁,会不会出现线程问题(依照本人的理解应该不会,由于本人才疏学浅,还望不吝赐教,另外该方法的实现是参考<<java并发编程实践>>)。
public class Cache<K, V> {
	
	private final ConcurrentHashMap<K, FutureTask<V>> cache = 
								new ConcurrentHashMap<K, FutureTask<V>>();
	
	private final ReadWriteLock lock = new ReentrantReadWriteLock();
	private final Lock readLock = lock.readLock();
//	private final Lock writeLock = lock.writeLock();
	
	public V addElement(final K key, final V value) {
		FutureTask<V> f = cache.get(key);
		while (true) {
			if (null == f) {
				Callable<V> eval = new Callable<V>() {

					@Override
					public V call() throws Exception {
						//此处实现比较简单,但是如果创建一个V的对象需要比较消耗性能的话,
						//这种缓存实现就有明显的优势
						return value;
					}
					
				};
				FutureTask<V> future = new FutureTask<V>(eval);
				f = cache.putIfAbsent(key, future);
				if (null == f) {
					f = future;
					future.run();
				}
			}
			
			try {
				return f.get();
			} catch (Exception e) {
				e.printStackTrace();
			}
		} 
	}
	
	public V getElement(K key) {
		try {
			readLock.lock();
			return cache.get(key).get();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			readLock.unlock();
		}
		return null;
	}
}

   在具体实现中getElement()方法使用了读锁从而支持并发读,在addElement()方法中没有加任何锁依靠FutureTask和实现逻辑来实现线程安全。如上实现优势: 当线程进入addElement方法后,首先判断对应key的Futuretask是否已经存在,如果不存在表明是第一次添加,然后利用Callable和Futuretask来创建一个value对象(如果创建value对象的代价非常昂贵的话,更能体现该实现的优势).假如在第一个线程正在创建value的过程中,addElement方法进入第二个线程,此时第二个线程会首先判断key对应的Futuretask是否已经存在,如不存在则利用Callable和Futuretask来创建一个value对象。试想此时的最好实现就是第二个线程判断对象是否存在或者是否正在创建,如果存在或正在创建那么最好的办法就是等待第一个线程创建完成后直接来分享胜利的果实即可,不需要再进行创建后再依靠concurrentHashMap的putIfAbsent方法来判断是否需要加入Map中,这种实现最明显的优势就是节约了创建一个复杂对象的开销,这一点也是该实现的精华所在.
以上是本人对这个方法的理解,还望各位多多拍砖。。。。。。。。
分享到:
评论
7 楼 kazy 2010-06-08  
从另外的地方抄个例子过来,

ConcurrentHashMap<String,String> map; 
String getString(String name) { 
    String x = map.get(name); 
    if (x == null) { 
        x = new String(); 
        map.put(name, x); 
    } 
    return x; 
}

如果你只调用get(),或只调用put()时,ConcurrentHashMap是线程安全的。
但是,在你调用完get后,调用put之前,
如果有另外一个线程调用了map.put(name, x),
你再去执行map.put(name,x),
就很可能把前面的操作结果覆盖掉了。
所以,即使在线程安全的情况下,
你还是有可能违反原子操作的规则。
6 楼 wantofly_gj 2010-06-08  
缓存么,就是要复用.你这里就是要复用每个key对应的value值,而你在Map中使用FutureTask而不是直接存value,只是为了占位(避免两个线程同时检查到key不存在,然后同时创建两个value).呵呵.我看了半天才明白:)
问题1:读写再入锁是没用的,putIfAbsent 方法其实就已经实现了防止同时写入的可能了(第一个线程占位之后,第二个线程就不可能再写入了).
问题2: while(true)实在没看明白??
问题3:之前beneo 说的 addElement 这个方法接口上面,final V value 应该改成 Callable<V> callable吧? 你传入的value值转了一圈就被直接返回了,没用啊
5 楼 icanfly 2010-06-08  
cjmcn-sh 写道
我的经验是,只要涉及到写入就需要进入临界区操作,读操作不需要加锁。

感觉上对读操作加锁没有必要,楼主可以看看google collections,
Google Collections中的MapMaker融合了Weak Reference,线程安全,高并发性能,异步超时清理,自定义构建元素等强大功能于一身。

不对吧? 临界区数据不管读写都得有保护,写入保护是保证在写入的时候没有其它干扰,而读取的时候保护是保证此时读取到的是最新写入的数据。

不过对于缓存级别的,不注重实时更新和数据有效性 这个可以作适当放宽。大不了缓存读取不到可以读数据库,大不了数据可以是一段时间前的。如果是要求数据实时性准确性非常高的话。读写都得加锁,保证实时读写
4 楼 soongbo 2010-06-08  
cjmcn-sh 写道
我的经验是,只要涉及到写入就需要进入临界区操作,读操作不需要加锁。

感觉上对读操作加锁没有必要,楼主可以看看google collections,
Google Collections中的MapMaker融合了Weak Reference,线程安全,高并发性能,异步超时清理,自定义构建元素等强大功能于一身。

针对读加锁是不应该,这里需要修正!而我想重点和大家探讨的是addElement方法在不加锁的前提下,使用如上源代码,是否能够实现线程安全???
3 楼 cjmcn-sh 2010-06-08  
我的经验是,只要涉及到写入就需要进入临界区操作,读操作不需要加锁。

感觉上对读操作加锁没有必要,楼主可以看看google collections,
Google Collections中的MapMaker融合了Weak Reference,线程安全,高并发性能,异步超时清理,自定义构建元素等强大功能于一身。
2 楼 soongbo 2010-06-08  
beneo 写道
1. 真的不是很清楚你这个使用环境
2. ConcurrentHashMap 本身就是线程安全的,所以cache.get(key)是不会出现线程安全问题,至于后面的.get(),这是获得异步的计算结果,再结果没有出来之前一直阻塞,所以也是线程安全的。
所以你的getElement(),锁是不必要的。
3. addElement 这个方法接口上面,final V value 应该改成 Callable<V> callable吧。此外你的f.get();这个是干嘛?既然你要做缓存,当然是把任务提交就好了,需要读数据的那就用getElement读好了
4. addElement 不加锁是不行的,你得保证你判断没有k 到 添加k是个原子操作


谢谢拍砖,针对你提出的几点问题回答如下:
1.该实现知识模拟一个缓存
2.cache.get(key)本来是线程安全的,这一点谢谢指正
3.addElement方法中用到f.get()可以返回值,同时也可以利用它实现轮询的结束(当然也可以添加break直接结束轮询)。
4.其实我贴出这段代码的主要目的就是想和大家讨论addElement方法是否可以不加锁利用Futuretask的包装来实现线程安全。在发帖的的原文中我写了一下内容" 当线程进入addElement方法后,首先判断对应key的Futuretask是否已经存在,如果不存在表明是第一次添加,然后利用Callable和Futuretask来创建一个value对象(如果创建value对象的代价非常昂贵的话,更能体现该实现的优势).假如在第一个线程正在创建value的过程中,addElement方法进入第二个线程,此时第二个线程会首先判断key对应的Futuretask是否已经存在,如不存在则利用Callable和Futuretask来创建一个value对象。试想此时的最好实现就是第二个线程判断对象是否存在或者是否正在创建,如果存在或正在创建那么最好的办法就是等待第一个线程创建完成后直接来分享胜利的果实即可,不需要再进行创建后再依靠concurrentHashMap的putIfAbsent方法来判断是否需要加入Map中,这种实现最明显的优势就是节约了创建一个复杂对象的开销,这一点也是该实现的精华所在. "
1 楼 beneo 2010-06-08  
1. 真的不是很清楚你这个使用环境
2. ConcurrentHashMap 本身就是线程安全的,所以cache.get(key)是不会出现线程安全问题,至于后面的.get(),这是获得异步的计算结果,再结果没有出来之前一直阻塞,所以也是线程安全的。
所以你的getElement(),锁是不必要的。
3. addElement 这个方法接口上面,final V value 应该改成 Callable<V> callable吧。此外你的f.get();这个是干嘛?既然你要做缓存,当然是把任务提交就好了,需要读数据的那就用getElement读好了
4. addElement 不加锁是不行的,你得保证你判断没有k 到 添加k是个原子操作

相关推荐

    高速缓存实现源码

    并发访问控制是高速缓存实现中的另一大挑战。在Java中,可以使用synchronized关键字或者java.util.concurrent包中的工具类如ReentrantLock、Semaphore等来实现线程安全。例如,当多个线程同时尝试读写缓存时,需要...

    实验六 采用高速缓存实现文件读写

    下面是一个简单的示例程序,展示了如何使用高速缓存实现文件读写。 首先,我们需要建立两个文件句柄,一个用于读取源文件,另一个用于写入目标文件。然后,我们可以使用 ReadFile 函数读取源文件的内容,并将其写入...

    高速缓存文件读写.cpp

    操作系统实验之第二高速缓存方式实现文件读写操作。

    05.高级计算机系统结构_高速缓存.ppt

    正确的高速缓存实现可以大大提高系统性能。 高速缓存是计算机系统结构中的一个关键组件,它可以大大提高系统性能。通过了解高速缓存的原理、结构和实现,我们可以更好地设计和实现高速缓存,以提高系统性能。

    高速缓存调度问题C++实现(opt方法)

    高速缓存调度问题的C++实现代码,采用最优的opt方法。代码注释详实,可读性好。

    高速缓存(Cache)的Verilog代码

    该工程包含数据缓存D_Cache和指令缓存I_Cache的Verilog代码和仿真文件,Cache的详细技术参数包含在.v文件的注释中。 直接相连16KB D_Cache Cache写策略: 写回法+写分配 (二路)组相连16KB I_Cache Cache替换策略: ...

    win2k内核高速缓存

    它通过一组例程和数据结构来实现数据的快速访问,包括读取和写入文件数据到内存中的高速缓存区。 计算系统高速缓存的大小是高速缓存管理的关键部分。高速缓存的大小可以是虚拟的也可以是物理的。虚拟大小指的是缓存...

    多处理器系统缓存一致性的分析.pdf

    在多处理器系统中,处理器之间通过高速缓存实现通信。高速缓存的一致性问题是多处理器系统设计中的一个重要挑战。为了在访问时间上与高速的处理器相匹配,多处理器系统要使用高速缓存。高速缓存能够提高处理器对...

    高速缓存知识

    **高速缓存**(Cache)是一种特殊类型的存储器子系统,它通过复制频繁使用的数据来实现快速访问的目的。Cache通常采用比主内存(RAM)更快但成本更高的静态随机存取存储器(SRAM)作为物理介质。 #### Cache的基本...

    利用高速缓存调度算法实现通讯信息的缓冲

    本项目通过VB6(Visual Basic 6)编程环境,结合Access2003数据库,实现了利用高速缓存调度算法对通讯信息进行缓冲,以提高数据传输效率和系统响应速度。以下是关于这一主题的详细知识解释。 首先,高速缓存(Cache...

    数据高速缓存区命中率

    数据高速缓存区命中率是衡量数据库性能的关键指标之一,特别是在高并发的环境中,缓存的效率直接影响到系统的响应速度和资源利用率。本文档将深入探讨数据高速缓存区(Buffer Cache)的管理与优化策略,以提升其命中...

    单片机与DSP中的高速数据采集系统中高速缓存与海量缓存的实现

    21065L的并行多通道数据采集板上高速采样缓存的设计与电路结构,给出了采用FPGA实现通道复用和采样数据预处理,从而构造16MB的SDRAM海量缓存以将高速缓存中的多批次采样数据经AD-21065L倒入SDRAM存储的实现方法。...

    ARM高速缓存(Cache)Verilog代码 包含ISE工程

    该工程包含数据缓存D_Cache和指令缓存I_Cache的Verilog代码和仿真文件,附带可运行的ISE工程文件,Cache的详细技术参数包含在.v文件的注释中。 直接相连16KB D_Cache Cache写策略: 写回法+写分配 (二路)组相连16KB ...

    C#读取web.config配置,建立高速缓存机制

    综上所述,通过web.config和Application对象构建的高速缓存机制,可以在.NET应用程序中实现对全局共享数据的有效管理和高速访问,从而提高应用程序的运行效率。不过,也需要合理规划缓存策略,避免造成资源的浪费或...

    单片机与DSP中的高速数据采集的高速缓存和海量缓存实现

    21065L的并行多通道数据采集板上高速采样缓存的设计与电路结构,给出了采用FPGA实现通道复用和采样数据预处理,从而构造16MB的SDRAM海量缓存以将高速缓存中的多批次采样数据经AD-21065L倒入SDRAM存储的实现方法。...

    高速数据采集系统中高速缓存与海量缓存的实现可用.pdf

    高速数据采集系统中高速缓存与海量缓存的实现可用.pdf

    现代体系结构上的UNIX系统--内核程序员的SMP和Caching技术 原书名: UNIX Systems for Modern Architectures

    第一部分“高速缓存存储系统”介绍了高速缓存体系结构、术语和概念,详细考察了4种常见的高速缓存实现——3种虚拟高速缓存的变体和物理高速缓存。第二部分“多处理机系统”讨论了调整单处理机内核的实现,使之适合于...

    vxWorks下的高速缓存存储器一致性问题解决方案

    ### vxWorks下的高速缓存存储器一致性问题解决方案 #### 概述 在嵌入式系统开发领域,实时操作系统(RTOS)的应用极为广泛。其中,**Wind River Systems** 公司的 **vxWorks** 是目前市场上最先进且应用广泛的实时...

Global site tag (gtag.js) - Google Analytics