`
donlianli
  • 浏览: 339227 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
Group-logo
Elasticsearch...
浏览量:217971
社区版块
存档分类
最新评论

正确读取与设置缓存方法

阅读更多

 

前言:
代码简洁与性能高效无法两全其美,本文章专注于大并发程序的性能,如果您追求代码简洁,本文章可能不太适合,因为本文章主要讨论如何写出在高并发下也能运行很好的代码。
 

     如果你感觉到缓存的重要性,那么,恭喜你,你的技术水平已经从初级上升了一个层次,意识到性能的重要性。你不再仅限于完成用户提出的功能,而是更注重提高系统的性能和软件的质量了。但是,仅仅在软件中随便加一个memcache或者osCache包就以为能够解决性能问题的话,那你就大错特错了。缓存只是提高性能一小步,提高性能更多是从设计层次来提高,但必有的编程技巧也是解决性能问题的一个主要因素。

本文章主要从如何查询及构建缓存开始,主要参考了Java Concurrency In Practice的一些章节,及网上的一些资料,结合实际的项目,做了一些应用。

首先,你一般设置缓存是否是这样写的呢?

//计算缓存的key
String cacheKey = getCacheKey(param1,param2);
//查询memcached
List<Long> list = (List<Long>)memcached.get(cacheKey);
if(list == null){
	//memcache 已经失效或者不存在,去查询数据库
	list = getFromDB(param1,param2);
	memcached.set(list,5*60);
	return list;
}

 这个方法在并发小的时候,应该不存在问题,但是当是一个高并发的系统时,那么这样的写法可能会导致缓存失效时,向数据库发起多个查询,然后查询完之后,还要向memcache Set多次。为什么,因为在如果同时过来10个请求,都发现缓存中没有数据(list == null),那么就都会去查询数据库,然后直到其中一个最先获得结果的线程,将结果设置到memcache,之后到来的线程,才会走缓存,但已经进来的线程,则还会继续查数据库,然后再将结果设置到memcache,这显然是我们不想看到的。那么如何处理呢,在方法上面加synchronized锁?开销太大。

这时,我们可以看看专家的意见,在设计高效的线程安全的缓存--JCIP5.6读书笔记 中讲了一种方法,可以既可以不使用锁,又保证多个线程同时请求时只有一个线程会访问数据库执行查询,其他线程都只读取计算结果的方法。如果你对里面讲的内容一头雾水的话,那么,你可以看看我写的这个示例,你只需要构建一个自己的Callable类,就能正确的设置与读取缓存。

 

假设concurrentService是一个先读缓存,没有缓存则读取数据库的方法,其代码如下:

	public List<Long> concurrentService(int para1,int param2){
		long beginTime = System.nanoTime();
		final String cacheKey = "IamKey";
		List<Long> list = (List<Long>)memCachedClient.get(cacheKey);
		if(list == null){
			Callable<Object> caller = new Callable<Object>() {
				public Object call() throws InterruptedException {
					System.out.println(" go to dao or rmi");
					List<Long> list = new ArrayList<Long>();
					list.add(1l);list.add(2l);
					//将计算结果缓存
					System.out.println("结果计算完毕,存入分布式缓存中");
					memCachedClient.set(cacheKey, 5*60, list);
//					Thread.sleep(500);
					//计算结果,通常是访问数据库或者远程服务
					return list;
				}
			};
			List<Long> result = (List<Long>)TaskUtils.getInTask(cacheKey,caller);
			long end = System.nanoTime();
			useTimes.add(end-beginTime);
			return result;
		}
		else {
			System.out.println("1.缓存命中,直接返回");
			long end = System.nanoTime();
			useTimes.add(end-beginTime);
			return list;
		}
	}

 

 其中的TaskUtils.getInTask定义如下:

import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.FutureTask;

public class TaskUtils {
	private static final ConcurrentMap<String, FutureTask<Object>> cache = new ConcurrentHashMap<String, FutureTask<Object>>();
	public static Object getInTask(String cacheKey, Callable<Object> caller) {
		System.out.println("1.缓存未命中,将查询数据库或者调用远程服务");
		//未命中缓存,开始计算
		FutureTask<Object> f = cache.get(cacheKey);
		if (f == null) {
			FutureTask<Object> ft = new FutureTask<Object>(caller);
			f = cache.putIfAbsent(cacheKey, ft);
			if (f == null) {
				System.out.println("2.任务未命中,将查询数据库或者调用远程服务");
				f = ft;
				ft.run();
			}
		}
		else {
			System.out.println("2.任务命中,直接从缓存取结果");
		}
		try {
			Object result = f.get();
			System.out.println("取回的结果result:"+result);
			return result;
		} catch (Exception e) {
			e.printStackTrace();
		}
		finally{
			//最后将计算任务去掉,虽然已经移除任务对象,但其他线程
			//仍然能够获取到计算的结果,直到所有引用都失效,被垃圾回收掉
			boolean success = cache.remove(cacheKey,f);
			System.out.println(success);
		}
		return null;
	}
}

       经过测试,使用这种方法读取与设置缓存,比使用synchronized方法和锁定键值的方法要快3-10倍,不信大家可以试试。

        有人可能有疑问,如果并发的线程很多,同时都没有命中缓存,那么不就会产生很多Callable<Object>对象吗?这样岂不会浪费很大内存吗?其实,我们仔细分析一下代码,可以看到Callable对象不管创建了多少,但最终经过putIfAbsent方法之后,就留下了一个有效的对象,其他的对象都成为失效对象,随时可以被GC掉。因此,使用这种方法,并不会造成JVM的内存溢出。

     另外,Callable<Object>就是一个普通的对象,跟线程一点关系都没有,里面虽然包括了一个runnable方法,但是并不是说这个会启动一个线程。里面的runnable方法在本代码中是在调用者线程中执行,但执行结果共享给了其他没有命中缓存的线程。

        赶紧回去review你们项目的代码吧,你们设置缓存的方式对吗?

        实际上这个TaskUtil的方法只是使用了两个重要的并发工具类,一个是ConcurrentMap,主要支持并发中经常使用的putIfAbsent方法,和一个FutureTask对象,这个对象的get方法能够阻塞调用者线程,直到结果可用。

 

 

对这类话题感兴趣?欢迎发送邮件至donlianli@126.com

 

关于我:邯郸人,擅长Java,Javascript,Extjs,oracle sql。

 

更多我之前的文章,可以访问:http://hi.baidu.com/donlian

 

1
1
分享到:
评论
38 楼 lvwenwen 2013-07-22  
正确读取与设置缓存方法
37 楼 dzxiang 2013-07-10  
406657836 写道
dzxiang 写道
406657836 写道
dzxiang 写道
406657836 写道
dzxiang 写道
406657836 写道
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
晕,好像还是有问题,看样子不能太随意了,再发一个吧:
		......
		private static final ReentrantLock lock = new ReentrantLock();  
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			lock.lock();
			try{
				list = (List<Long>)memcached.get(cacheKey);  			
				if(list == null){	
			        //memcache 已经失效或者不存在,去查询数据库  
			        list = getFromDB(param1,param2);  
			        memcached.set(list,5*60);  
				}
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list; 


之前发的那两个完全是坑。这一个也不高效,锁竞争太激烈了(相比ConcurrentHashMap).不过你这个节约了内存开销,通常情况下还是不推荐。更喜欢楼主的ConcurrentHashMap,用空间换时间!

这种做法对锁的竞争只发生于 list == null 的情况下,谈不上竞争多激烈吧? 把代码格式化一下,是不是更清楚一点。

补充:当getFromDB方法(初始化缓存数据)执行时间太长的时候,如果这时候并发的请求非常多,会出现“406657836”所说的竞争激烈的情况,但也可以通过设定超时来解决,代码如下:
		......
		private static final ReentrantLock lock = new ReentrantLock();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁
			try{
				//如果其它线程已经获取锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

回复这个贴子的目的就是想和大家探讨一下这种简单的做法是否可以代替楼主提出的方案,Java Concurrency in Practice的5.6章的例子我也看过,肯定是可行的,但我个人觉得有更简单的解决方案,不用搞的这么复杂。
当然,在实际的项目中,初始化缓存的对象如果比较耗时,我通常在系统启动时用独立的FutureTask加载这个缓存数据,而不是在使用的时候才去检测加载。


可能你没有明白我说的 锁竞争激烈的意思。ConcurrentHashMap 是支持多线程并发写入的,只要key不碰撞到同一segment就不会出现锁竞争。也就是说同时多个线程在写ConcurrentHashMap,而您的这个实现,每个线程都要获得锁才能够写入。都是去竞争的同一把锁,所以锁的竞争更激烈些。楼主是用的默认的ConcurrentHashMap构造函数,最好的情况可以支持16个线程并发写(默认segment为16)。

嗯,简单说:就是使用ConcurrentHashMap尽量让不同的KEY使用不同的锁,每个线程的写入肯定是要获得锁才能写入的,但是它们可以使用不同的锁避免竞争。(每人一个小柜子,各有各的锁,它们可以同时开锁,而不是共用一个大柜子,共用一把锁,需要排除等待)。
很显然,我给出的代码现在主要的问题就是:所有的KEY使用的是同一把锁,当不同的KEY需要写入缓存时,竞争过于激烈,如果改成ConcurrentHashMap存储不同KEY的ReentrantLock,是否可以达到楼主同样的效果?

备注部分有笔误,更正一下:
每个人(KEY)一个小柜子,各有各的锁,需要放东西时,不同的人(KEY)可以同时开锁,而不是共用一个大柜子,共用同一把锁,每个人(KEY)放东西时都需要排队等待同一把锁。


你还是没有明白我的意思!你的lock是一个全局的,也就是所有的key都要竞争这个锁才能去操作memcache,至于你说的每个key各有个的锁?你怎么去为每个key分配的锁?没有看到,也不现实!

你根本没有仔细看过我的回复。

好吧!会有人懂的!

我以为回复的够清楚了,你如果一定要看代码,那好吧:
		......
		private static final ConcurrentMap<String, ReentrantLock> lockCache = new ConcurrentHashMap<String, ReentrantLock>();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁,不同的KEY,使用不同的锁
			final ReentrantLock lock = new ReentrantLock();
			ReentrantLock existLock = lockCache.putIfAbsent(cacheKey, lock);
			if(existLock!=null){
				lock = existLock;
			}
			try{
				//如果其它线程已经获取同一个KEY的锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

这里面为每个KEY分配一个锁,我不明白你说的“不现实”指哪方面? 还请指教。


神啊!你真觉得你这就是对单个key加锁了?请问你操作ConcurrentHashMap的 putIfAbsent锁在干嘛?而且后面的remove锁在干嘛?你这里的消耗就和楼主的程序一样了,而且,你除了要用ConcurrentHashMap的锁外,还要加锁,你再看看楼主的程序,不尽简单,而且还性能好(没有第二个锁)。

有这么夸张吗? 连神都扯出来了,楼主的程序还不一样是两次锁操作,第一次f = cache.putIfAbsent(cacheKey, ft), 第二次:Object result = f.get(); 虽然表面没有lock的字眼,但跟踪到ConcurrentHashMap和FutureTask代码内部,本质还不是一样?
另外你说的remove,楼主的代码中还有一个 boolean success = cache.remove(cacheKey,f);   这其实又是一次锁操作,而我给出的代码个人认为不需要remove,相对而言不是少了一次锁操作?

不remove 等着oom。你的ConcurrentHashMap无限扩大?f.get()是有值了直接返回,而你的trylock即使有值了,也可能悲观的等10毫秒(别不把10毫秒放眼里)而且load数据慢还要耗cpu去tryLock(当load需要N个10毫秒的时候),你可以并发模拟下,看看程序的吞吐量。

不remove 等着oom。你的ConcurrentHashMap无限扩大?
=========
楼主的方案当然需要remove,但我的不需要,我的ConcurrentHashMap存储的是cacheKey与ReentrantLock,cacheKey一定是有限的,内存完全可控,我还没见过,甚至都没听说过cacheKey值无限的系统,存储cacheKey导致OOM? 那得是多大的系统?

而你的trylock即使有值了,也可能悲观的等10毫秒
=====
你这样理解ReentrantLock的tryLock方法,会误导其它人的,建议看看ReentrantLock源码。

楼主给的方案已经接近完美了,我的以上发言只是想提出另外一个方案讨论下看是否实现了楼主相同的目标。
楼主方案中的Object result = f.get(); 我倒是建议设定一个超时时间,如果短期有太多相同cacheKey请求,而读取数据(getFromDB)过久的话,可能会造成问题。 如果是我,我会使用Object result = f.get(XX,TimeUnit.SECONDS)这类方法,规避getFromDB对整个系统的影响;
嗯 关于tryLock我的表述确实有误,应该说当数据可用了也只有其中一个等待的线程获取锁被唤醒(这通常还要取决于线程调度策略),其他线程仍然在tryLock上等待,在tryLock上等待的线程可能很多。要以一一的判断memcache的数据状态(增加了memcache的负载,楼主在这里 根本不读memcache   你这里仍然是有多少个等待线程就读多少次memcache,虽然是内存访问,毕竟有开销),在这些判断中极有可能是很多tryLock等待超过10毫秒,无形之中也是消耗。

cachekey 也完全有可能oom,因为一般key都有过期时间,过期一个key就要增加一个内容(再重建缓存的时候),长期运行下去,有一千万个key,就会在某个时间内有一千万个键值对(即使是热数据只有10万,其他数据只要被再次缓存就要增加键值对,这不可忽视的)。如果系统不重启一直累加,内存消耗,即使不oom也可能使老年代内存一直被耗着而导致频繁gc,而每次gc又发现这块内存不可回收,反复之,性能,吞吐量不言而喻。当然你可以重启来解决这个问题,但是真的愿意这样做吗?

况且还有分布式缓存,也就是缓存的数据量确实可以很大,要不然也不用做分布式缓存了。

确实f.get加一个超时时间是一个很好的建议,不能无限等待下去,谁知道数据库那边出现什么故障了。不可用的时候要返回提示,要不然是非常糟糕的用户体验,也会使得web服务器的连接数暴涨,甚至宏机。

呵呵,我们这是对于知识的探讨呵,很高兴能遇到共同兴趣的人,我加你关注!还多多讨论。对于tryLock的描述确实错了,其实之前是知道它的机制,但是回答的时候欠缺思考。


,与你的讨论使我对某些问题的看法有了改变,看样子还是得多讨论才能有所进步,特别是对于cacheKey,至少应该定时清理下过期cacheKey。
另外,我的代码中还有一个BUG:当lock.tryLock(10, TimeUnit.SECONDS)未能获得锁的情况下,是不需要unlock()的,在finally里应该做个判断。
36 楼 406657836 2013-07-10  
dzxiang 写道
406657836 写道
dzxiang 写道
406657836 写道
dzxiang 写道
406657836 写道
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
晕,好像还是有问题,看样子不能太随意了,再发一个吧:
		......
		private static final ReentrantLock lock = new ReentrantLock();  
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			lock.lock();
			try{
				list = (List<Long>)memcached.get(cacheKey);  			
				if(list == null){	
			        //memcache 已经失效或者不存在,去查询数据库  
			        list = getFromDB(param1,param2);  
			        memcached.set(list,5*60);  
				}
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list; 


之前发的那两个完全是坑。这一个也不高效,锁竞争太激烈了(相比ConcurrentHashMap).不过你这个节约了内存开销,通常情况下还是不推荐。更喜欢楼主的ConcurrentHashMap,用空间换时间!

这种做法对锁的竞争只发生于 list == null 的情况下,谈不上竞争多激烈吧? 把代码格式化一下,是不是更清楚一点。

补充:当getFromDB方法(初始化缓存数据)执行时间太长的时候,如果这时候并发的请求非常多,会出现“406657836”所说的竞争激烈的情况,但也可以通过设定超时来解决,代码如下:
		......
		private static final ReentrantLock lock = new ReentrantLock();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁
			try{
				//如果其它线程已经获取锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

回复这个贴子的目的就是想和大家探讨一下这种简单的做法是否可以代替楼主提出的方案,Java Concurrency in Practice的5.6章的例子我也看过,肯定是可行的,但我个人觉得有更简单的解决方案,不用搞的这么复杂。
当然,在实际的项目中,初始化缓存的对象如果比较耗时,我通常在系统启动时用独立的FutureTask加载这个缓存数据,而不是在使用的时候才去检测加载。


可能你没有明白我说的 锁竞争激烈的意思。ConcurrentHashMap 是支持多线程并发写入的,只要key不碰撞到同一segment就不会出现锁竞争。也就是说同时多个线程在写ConcurrentHashMap,而您的这个实现,每个线程都要获得锁才能够写入。都是去竞争的同一把锁,所以锁的竞争更激烈些。楼主是用的默认的ConcurrentHashMap构造函数,最好的情况可以支持16个线程并发写(默认segment为16)。

嗯,简单说:就是使用ConcurrentHashMap尽量让不同的KEY使用不同的锁,每个线程的写入肯定是要获得锁才能写入的,但是它们可以使用不同的锁避免竞争。(每人一个小柜子,各有各的锁,它们可以同时开锁,而不是共用一个大柜子,共用一把锁,需要排除等待)。
很显然,我给出的代码现在主要的问题就是:所有的KEY使用的是同一把锁,当不同的KEY需要写入缓存时,竞争过于激烈,如果改成ConcurrentHashMap存储不同KEY的ReentrantLock,是否可以达到楼主同样的效果?

备注部分有笔误,更正一下:
每个人(KEY)一个小柜子,各有各的锁,需要放东西时,不同的人(KEY)可以同时开锁,而不是共用一个大柜子,共用同一把锁,每个人(KEY)放东西时都需要排队等待同一把锁。


你还是没有明白我的意思!你的lock是一个全局的,也就是所有的key都要竞争这个锁才能去操作memcache,至于你说的每个key各有个的锁?你怎么去为每个key分配的锁?没有看到,也不现实!

你根本没有仔细看过我的回复。

好吧!会有人懂的!

我以为回复的够清楚了,你如果一定要看代码,那好吧:
		......
		private static final ConcurrentMap<String, ReentrantLock> lockCache = new ConcurrentHashMap<String, ReentrantLock>();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁,不同的KEY,使用不同的锁
			final ReentrantLock lock = new ReentrantLock();
			ReentrantLock existLock = lockCache.putIfAbsent(cacheKey, lock);
			if(existLock!=null){
				lock = existLock;
			}
			try{
				//如果其它线程已经获取同一个KEY的锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

这里面为每个KEY分配一个锁,我不明白你说的“不现实”指哪方面? 还请指教。


神啊!你真觉得你这就是对单个key加锁了?请问你操作ConcurrentHashMap的 putIfAbsent锁在干嘛?而且后面的remove锁在干嘛?你这里的消耗就和楼主的程序一样了,而且,你除了要用ConcurrentHashMap的锁外,还要加锁,你再看看楼主的程序,不尽简单,而且还性能好(没有第二个锁)。

有这么夸张吗? 连神都扯出来了,楼主的程序还不一样是两次锁操作,第一次f = cache.putIfAbsent(cacheKey, ft), 第二次:Object result = f.get(); 虽然表面没有lock的字眼,但跟踪到ConcurrentHashMap和FutureTask代码内部,本质还不是一样?
另外你说的remove,楼主的代码中还有一个 boolean success = cache.remove(cacheKey,f);   这其实又是一次锁操作,而我给出的代码个人认为不需要remove,相对而言不是少了一次锁操作?

不remove 等着oom。你的ConcurrentHashMap无限扩大?f.get()是有值了直接返回,而你的trylock即使有值了,也可能悲观的等10毫秒(别不把10毫秒放眼里)而且load数据慢还要耗cpu去tryLock(当load需要N个10毫秒的时候),你可以并发模拟下,看看程序的吞吐量。

不remove 等着oom。你的ConcurrentHashMap无限扩大?
=========
楼主的方案当然需要remove,但我的不需要,我的ConcurrentHashMap存储的是cacheKey与ReentrantLock,cacheKey一定是有限的,内存完全可控,我还没见过,甚至都没听说过cacheKey值无限的系统,存储cacheKey导致OOM? 那得是多大的系统?

而你的trylock即使有值了,也可能悲观的等10毫秒
=====
你这样理解ReentrantLock的tryLock方法,会误导其它人的,建议看看ReentrantLock源码。

楼主给的方案已经接近完美了,我的以上发言只是想提出另外一个方案讨论下看是否实现了楼主相同的目标。
楼主方案中的Object result = f.get(); 我倒是建议设定一个超时时间,如果短期有太多相同cacheKey请求,而读取数据(getFromDB)过久的话,可能会造成问题。 如果是我,我会使用Object result = f.get(XX,TimeUnit.SECONDS)这类方法,规避getFromDB对整个系统的影响;
嗯 关于tryLock我的表述确实有误,应该说当数据可用了也只有其中一个等待的线程获取锁被唤醒(这通常还要取决于线程调度策略),其他线程仍然在tryLock上等待,在tryLock上等待的线程可能很多。要以一一的判断memcache的数据状态(增加了memcache的负载,楼主在这里 根本不读memcache   你这里仍然是有多少个等待线程就读多少次memcache,虽然是内存访问,毕竟有开销),在这些判断中极有可能是很多tryLock等待超过10毫秒,无形之中也是消耗。

cachekey 也完全有可能oom,因为一般key都有过期时间,过期一个key就要增加一个内容(再重建缓存的时候),长期运行下去,有一千万个key,就会在某个时间内有一千万个键值对(即使是热数据只有10万,其他数据只要被再次缓存就要增加键值对,这不可忽视的)。如果系统不重启一直累加,内存消耗,即使不oom也可能使老年代内存一直被耗着而导致频繁gc,而每次gc又发现这块内存不可回收,反复之,性能,吞吐量不言而喻。当然你可以重启来解决这个问题,但是真的愿意这样做吗?

况且还有分布式缓存,也就是缓存的数据量确实可以很大,要不然也不用做分布式缓存了。

确实f.get加一个超时时间是一个很好的建议,不能无限等待下去,谁知道数据库那边出现什么故障了。不可用的时候要返回提示,要不然是非常糟糕的用户体验,也会使得web服务器的连接数暴涨,甚至宏机。

呵呵,我们这是对于知识的探讨呵,很高兴能遇到共同兴趣的人,我加你关注!还多多讨论。对于tryLock的描述确实错了,其实之前是知道它的机制,但是回答的时候欠缺思考。

35 楼 dzxiang 2013-07-10  
406657836 写道
dzxiang 写道
406657836 写道
dzxiang 写道
406657836 写道
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
晕,好像还是有问题,看样子不能太随意了,再发一个吧:
		......
		private static final ReentrantLock lock = new ReentrantLock();  
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			lock.lock();
			try{
				list = (List<Long>)memcached.get(cacheKey);  			
				if(list == null){	
			        //memcache 已经失效或者不存在,去查询数据库  
			        list = getFromDB(param1,param2);  
			        memcached.set(list,5*60);  
				}
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list; 


之前发的那两个完全是坑。这一个也不高效,锁竞争太激烈了(相比ConcurrentHashMap).不过你这个节约了内存开销,通常情况下还是不推荐。更喜欢楼主的ConcurrentHashMap,用空间换时间!

这种做法对锁的竞争只发生于 list == null 的情况下,谈不上竞争多激烈吧? 把代码格式化一下,是不是更清楚一点。

补充:当getFromDB方法(初始化缓存数据)执行时间太长的时候,如果这时候并发的请求非常多,会出现“406657836”所说的竞争激烈的情况,但也可以通过设定超时来解决,代码如下:
		......
		private static final ReentrantLock lock = new ReentrantLock();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁
			try{
				//如果其它线程已经获取锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

回复这个贴子的目的就是想和大家探讨一下这种简单的做法是否可以代替楼主提出的方案,Java Concurrency in Practice的5.6章的例子我也看过,肯定是可行的,但我个人觉得有更简单的解决方案,不用搞的这么复杂。
当然,在实际的项目中,初始化缓存的对象如果比较耗时,我通常在系统启动时用独立的FutureTask加载这个缓存数据,而不是在使用的时候才去检测加载。


可能你没有明白我说的 锁竞争激烈的意思。ConcurrentHashMap 是支持多线程并发写入的,只要key不碰撞到同一segment就不会出现锁竞争。也就是说同时多个线程在写ConcurrentHashMap,而您的这个实现,每个线程都要获得锁才能够写入。都是去竞争的同一把锁,所以锁的竞争更激烈些。楼主是用的默认的ConcurrentHashMap构造函数,最好的情况可以支持16个线程并发写(默认segment为16)。

嗯,简单说:就是使用ConcurrentHashMap尽量让不同的KEY使用不同的锁,每个线程的写入肯定是要获得锁才能写入的,但是它们可以使用不同的锁避免竞争。(每人一个小柜子,各有各的锁,它们可以同时开锁,而不是共用一个大柜子,共用一把锁,需要排除等待)。
很显然,我给出的代码现在主要的问题就是:所有的KEY使用的是同一把锁,当不同的KEY需要写入缓存时,竞争过于激烈,如果改成ConcurrentHashMap存储不同KEY的ReentrantLock,是否可以达到楼主同样的效果?

备注部分有笔误,更正一下:
每个人(KEY)一个小柜子,各有各的锁,需要放东西时,不同的人(KEY)可以同时开锁,而不是共用一个大柜子,共用同一把锁,每个人(KEY)放东西时都需要排队等待同一把锁。


你还是没有明白我的意思!你的lock是一个全局的,也就是所有的key都要竞争这个锁才能去操作memcache,至于你说的每个key各有个的锁?你怎么去为每个key分配的锁?没有看到,也不现实!

你根本没有仔细看过我的回复。

好吧!会有人懂的!

我以为回复的够清楚了,你如果一定要看代码,那好吧:
		......
		private static final ConcurrentMap<String, ReentrantLock> lockCache = new ConcurrentHashMap<String, ReentrantLock>();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁,不同的KEY,使用不同的锁
			final ReentrantLock lock = new ReentrantLock();
			ReentrantLock existLock = lockCache.putIfAbsent(cacheKey, lock);
			if(existLock!=null){
				lock = existLock;
			}
			try{
				//如果其它线程已经获取同一个KEY的锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

这里面为每个KEY分配一个锁,我不明白你说的“不现实”指哪方面? 还请指教。


神啊!你真觉得你这就是对单个key加锁了?请问你操作ConcurrentHashMap的 putIfAbsent锁在干嘛?而且后面的remove锁在干嘛?你这里的消耗就和楼主的程序一样了,而且,你除了要用ConcurrentHashMap的锁外,还要加锁,你再看看楼主的程序,不尽简单,而且还性能好(没有第二个锁)。

有这么夸张吗? 连神都扯出来了,楼主的程序还不一样是两次锁操作,第一次f = cache.putIfAbsent(cacheKey, ft), 第二次:Object result = f.get(); 虽然表面没有lock的字眼,但跟踪到ConcurrentHashMap和FutureTask代码内部,本质还不是一样?
另外你说的remove,楼主的代码中还有一个 boolean success = cache.remove(cacheKey,f);   这其实又是一次锁操作,而我给出的代码个人认为不需要remove,相对而言不是少了一次锁操作?

不remove 等着oom。你的ConcurrentHashMap无限扩大?f.get()是有值了直接返回,而你的trylock即使有值了,也可能悲观的等10毫秒(别不把10毫秒放眼里)而且load数据慢还要耗cpu去tryLock(当load需要N个10毫秒的时候),你可以并发模拟下,看看程序的吞吐量。

不remove 等着oom。你的ConcurrentHashMap无限扩大?
=========
楼主的方案当然需要remove,但我的不需要,我的ConcurrentHashMap存储的是cacheKey与ReentrantLock,cacheKey一定是有限的,内存完全可控,我还没见过,甚至都没听说过cacheKey值无限的系统,存储cacheKey导致OOM? 那得是多大的系统?

而你的trylock即使有值了,也可能悲观的等10毫秒
=====
你这样理解ReentrantLock的tryLock方法,会误导其它人的,建议看看ReentrantLock源码。

楼主给的方案已经接近完美了,我的以上发言只是想提出另外一个方案讨论下看是否实现了楼主相同的目标。
楼主方案中的Object result = f.get(); 我倒是建议设定一个超时时间,如果短期有太多相同cacheKey请求,而读取数据(getFromDB)过久的话,可能会造成问题。 如果是我,我会使用Object result = f.get(XX,TimeUnit.SECONDS)这类方法,规避getFromDB对整个系统的影响;

34 楼 406657836 2013-07-09  
dzxiang 写道
406657836 写道
dzxiang 写道
406657836 写道
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
晕,好像还是有问题,看样子不能太随意了,再发一个吧:
		......
		private static final ReentrantLock lock = new ReentrantLock();  
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			lock.lock();
			try{
				list = (List<Long>)memcached.get(cacheKey);  			
				if(list == null){	
			        //memcache 已经失效或者不存在,去查询数据库  
			        list = getFromDB(param1,param2);  
			        memcached.set(list,5*60);  
				}
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list; 


之前发的那两个完全是坑。这一个也不高效,锁竞争太激烈了(相比ConcurrentHashMap).不过你这个节约了内存开销,通常情况下还是不推荐。更喜欢楼主的ConcurrentHashMap,用空间换时间!

这种做法对锁的竞争只发生于 list == null 的情况下,谈不上竞争多激烈吧? 把代码格式化一下,是不是更清楚一点。

补充:当getFromDB方法(初始化缓存数据)执行时间太长的时候,如果这时候并发的请求非常多,会出现“406657836”所说的竞争激烈的情况,但也可以通过设定超时来解决,代码如下:
		......
		private static final ReentrantLock lock = new ReentrantLock();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁
			try{
				//如果其它线程已经获取锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

回复这个贴子的目的就是想和大家探讨一下这种简单的做法是否可以代替楼主提出的方案,Java Concurrency in Practice的5.6章的例子我也看过,肯定是可行的,但我个人觉得有更简单的解决方案,不用搞的这么复杂。
当然,在实际的项目中,初始化缓存的对象如果比较耗时,我通常在系统启动时用独立的FutureTask加载这个缓存数据,而不是在使用的时候才去检测加载。


可能你没有明白我说的 锁竞争激烈的意思。ConcurrentHashMap 是支持多线程并发写入的,只要key不碰撞到同一segment就不会出现锁竞争。也就是说同时多个线程在写ConcurrentHashMap,而您的这个实现,每个线程都要获得锁才能够写入。都是去竞争的同一把锁,所以锁的竞争更激烈些。楼主是用的默认的ConcurrentHashMap构造函数,最好的情况可以支持16个线程并发写(默认segment为16)。

嗯,简单说:就是使用ConcurrentHashMap尽量让不同的KEY使用不同的锁,每个线程的写入肯定是要获得锁才能写入的,但是它们可以使用不同的锁避免竞争。(每人一个小柜子,各有各的锁,它们可以同时开锁,而不是共用一个大柜子,共用一把锁,需要排除等待)。
很显然,我给出的代码现在主要的问题就是:所有的KEY使用的是同一把锁,当不同的KEY需要写入缓存时,竞争过于激烈,如果改成ConcurrentHashMap存储不同KEY的ReentrantLock,是否可以达到楼主同样的效果?

备注部分有笔误,更正一下:
每个人(KEY)一个小柜子,各有各的锁,需要放东西时,不同的人(KEY)可以同时开锁,而不是共用一个大柜子,共用同一把锁,每个人(KEY)放东西时都需要排队等待同一把锁。


你还是没有明白我的意思!你的lock是一个全局的,也就是所有的key都要竞争这个锁才能去操作memcache,至于你说的每个key各有个的锁?你怎么去为每个key分配的锁?没有看到,也不现实!

你根本没有仔细看过我的回复。

好吧!会有人懂的!

我以为回复的够清楚了,你如果一定要看代码,那好吧:
		......
		private static final ConcurrentMap<String, ReentrantLock> lockCache = new ConcurrentHashMap<String, ReentrantLock>();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁,不同的KEY,使用不同的锁
			final ReentrantLock lock = new ReentrantLock();
			ReentrantLock existLock = lockCache.putIfAbsent(cacheKey, lock);
			if(existLock!=null){
				lock = existLock;
			}
			try{
				//如果其它线程已经获取同一个KEY的锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

这里面为每个KEY分配一个锁,我不明白你说的“不现实”指哪方面? 还请指教。


神啊!你真觉得你这就是对单个key加锁了?请问你操作ConcurrentHashMap的 putIfAbsent锁在干嘛?而且后面的remove锁在干嘛?你这里的消耗就和楼主的程序一样了,而且,你除了要用ConcurrentHashMap的锁外,还要加锁,你再看看楼主的程序,不尽简单,而且还性能好(没有第二个锁)。

有这么夸张吗? 连神都扯出来了,楼主的程序还不一样是两次锁操作,第一次f = cache.putIfAbsent(cacheKey, ft), 第二次:Object result = f.get(); 虽然表面没有lock的字眼,但跟踪到ConcurrentHashMap和FutureTask代码内部,本质还不是一样?
另外你说的remove,楼主的代码中还有一个 boolean success = cache.remove(cacheKey,f);   这其实又是一次锁操作,而我给出的代码个人认为不需要remove,相对而言不是少了一次锁操作?

不remove 等着oom。你的ConcurrentHashMap无限扩大?f.get()是有值了直接返回,而你的trylock即使有值了,也可能悲观的等10毫秒(别不把10毫秒放眼里)而且load数据慢还要耗cpu去tryLock(当load需要N个10毫秒的时候),你可以并发模拟下,看看程序的吞吐量。
33 楼 dzxiang 2013-07-09  
406657836 写道
dzxiang 写道
406657836 写道
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
晕,好像还是有问题,看样子不能太随意了,再发一个吧:
		......
		private static final ReentrantLock lock = new ReentrantLock();  
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			lock.lock();
			try{
				list = (List<Long>)memcached.get(cacheKey);  			
				if(list == null){	
			        //memcache 已经失效或者不存在,去查询数据库  
			        list = getFromDB(param1,param2);  
			        memcached.set(list,5*60);  
				}
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list; 


之前发的那两个完全是坑。这一个也不高效,锁竞争太激烈了(相比ConcurrentHashMap).不过你这个节约了内存开销,通常情况下还是不推荐。更喜欢楼主的ConcurrentHashMap,用空间换时间!

这种做法对锁的竞争只发生于 list == null 的情况下,谈不上竞争多激烈吧? 把代码格式化一下,是不是更清楚一点。

补充:当getFromDB方法(初始化缓存数据)执行时间太长的时候,如果这时候并发的请求非常多,会出现“406657836”所说的竞争激烈的情况,但也可以通过设定超时来解决,代码如下:
		......
		private static final ReentrantLock lock = new ReentrantLock();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁
			try{
				//如果其它线程已经获取锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

回复这个贴子的目的就是想和大家探讨一下这种简单的做法是否可以代替楼主提出的方案,Java Concurrency in Practice的5.6章的例子我也看过,肯定是可行的,但我个人觉得有更简单的解决方案,不用搞的这么复杂。
当然,在实际的项目中,初始化缓存的对象如果比较耗时,我通常在系统启动时用独立的FutureTask加载这个缓存数据,而不是在使用的时候才去检测加载。


可能你没有明白我说的 锁竞争激烈的意思。ConcurrentHashMap 是支持多线程并发写入的,只要key不碰撞到同一segment就不会出现锁竞争。也就是说同时多个线程在写ConcurrentHashMap,而您的这个实现,每个线程都要获得锁才能够写入。都是去竞争的同一把锁,所以锁的竞争更激烈些。楼主是用的默认的ConcurrentHashMap构造函数,最好的情况可以支持16个线程并发写(默认segment为16)。

嗯,简单说:就是使用ConcurrentHashMap尽量让不同的KEY使用不同的锁,每个线程的写入肯定是要获得锁才能写入的,但是它们可以使用不同的锁避免竞争。(每人一个小柜子,各有各的锁,它们可以同时开锁,而不是共用一个大柜子,共用一把锁,需要排除等待)。
很显然,我给出的代码现在主要的问题就是:所有的KEY使用的是同一把锁,当不同的KEY需要写入缓存时,竞争过于激烈,如果改成ConcurrentHashMap存储不同KEY的ReentrantLock,是否可以达到楼主同样的效果?

备注部分有笔误,更正一下:
每个人(KEY)一个小柜子,各有各的锁,需要放东西时,不同的人(KEY)可以同时开锁,而不是共用一个大柜子,共用同一把锁,每个人(KEY)放东西时都需要排队等待同一把锁。


你还是没有明白我的意思!你的lock是一个全局的,也就是所有的key都要竞争这个锁才能去操作memcache,至于你说的每个key各有个的锁?你怎么去为每个key分配的锁?没有看到,也不现实!

你根本没有仔细看过我的回复。

好吧!会有人懂的!

我以为回复的够清楚了,你如果一定要看代码,那好吧:
		......
		private static final ConcurrentMap<String, ReentrantLock> lockCache = new ConcurrentHashMap<String, ReentrantLock>();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁,不同的KEY,使用不同的锁
			final ReentrantLock lock = new ReentrantLock();
			ReentrantLock existLock = lockCache.putIfAbsent(cacheKey, lock);
			if(existLock!=null){
				lock = existLock;
			}
			try{
				//如果其它线程已经获取同一个KEY的锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

这里面为每个KEY分配一个锁,我不明白你说的“不现实”指哪方面? 还请指教。


神啊!你真觉得你这就是对单个key加锁了?请问你操作ConcurrentHashMap的 putIfAbsent锁在干嘛?而且后面的remove锁在干嘛?你这里的消耗就和楼主的程序一样了,而且,你除了要用ConcurrentHashMap的锁外,还要加锁,你再看看楼主的程序,不尽简单,而且还性能好(没有第二个锁)。

有这么夸张吗? 连神都扯出来了,楼主的程序还不一样是两次锁操作,第一次f = cache.putIfAbsent(cacheKey, ft), 第二次:Object result = f.get(); 虽然表面没有lock的字眼,但跟踪到ConcurrentHashMap和FutureTask代码内部,本质还不是一样?
另外你说的remove,楼主的代码中还有一个 boolean success = cache.remove(cacheKey,f);   这其实又是一次锁操作,而我给出的代码个人认为不需要remove,相对而言不是少了一次锁操作?
32 楼 406657836 2013-07-09  
dzxiang 写道
406657836 写道
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
晕,好像还是有问题,看样子不能太随意了,再发一个吧:
		......
		private static final ReentrantLock lock = new ReentrantLock();  
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			lock.lock();
			try{
				list = (List<Long>)memcached.get(cacheKey);  			
				if(list == null){	
			        //memcache 已经失效或者不存在,去查询数据库  
			        list = getFromDB(param1,param2);  
			        memcached.set(list,5*60);  
				}
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list; 


之前发的那两个完全是坑。这一个也不高效,锁竞争太激烈了(相比ConcurrentHashMap).不过你这个节约了内存开销,通常情况下还是不推荐。更喜欢楼主的ConcurrentHashMap,用空间换时间!

这种做法对锁的竞争只发生于 list == null 的情况下,谈不上竞争多激烈吧? 把代码格式化一下,是不是更清楚一点。

补充:当getFromDB方法(初始化缓存数据)执行时间太长的时候,如果这时候并发的请求非常多,会出现“406657836”所说的竞争激烈的情况,但也可以通过设定超时来解决,代码如下:
		......
		private static final ReentrantLock lock = new ReentrantLock();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁
			try{
				//如果其它线程已经获取锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

回复这个贴子的目的就是想和大家探讨一下这种简单的做法是否可以代替楼主提出的方案,Java Concurrency in Practice的5.6章的例子我也看过,肯定是可行的,但我个人觉得有更简单的解决方案,不用搞的这么复杂。
当然,在实际的项目中,初始化缓存的对象如果比较耗时,我通常在系统启动时用独立的FutureTask加载这个缓存数据,而不是在使用的时候才去检测加载。


可能你没有明白我说的 锁竞争激烈的意思。ConcurrentHashMap 是支持多线程并发写入的,只要key不碰撞到同一segment就不会出现锁竞争。也就是说同时多个线程在写ConcurrentHashMap,而您的这个实现,每个线程都要获得锁才能够写入。都是去竞争的同一把锁,所以锁的竞争更激烈些。楼主是用的默认的ConcurrentHashMap构造函数,最好的情况可以支持16个线程并发写(默认segment为16)。

嗯,简单说:就是使用ConcurrentHashMap尽量让不同的KEY使用不同的锁,每个线程的写入肯定是要获得锁才能写入的,但是它们可以使用不同的锁避免竞争。(每人一个小柜子,各有各的锁,它们可以同时开锁,而不是共用一个大柜子,共用一把锁,需要排除等待)。
很显然,我给出的代码现在主要的问题就是:所有的KEY使用的是同一把锁,当不同的KEY需要写入缓存时,竞争过于激烈,如果改成ConcurrentHashMap存储不同KEY的ReentrantLock,是否可以达到楼主同样的效果?

备注部分有笔误,更正一下:
每个人(KEY)一个小柜子,各有各的锁,需要放东西时,不同的人(KEY)可以同时开锁,而不是共用一个大柜子,共用同一把锁,每个人(KEY)放东西时都需要排队等待同一把锁。


你还是没有明白我的意思!你的lock是一个全局的,也就是所有的key都要竞争这个锁才能去操作memcache,至于你说的每个key各有个的锁?你怎么去为每个key分配的锁?没有看到,也不现实!

你根本没有仔细看过我的回复。

好吧!会有人懂的!

我以为回复的够清楚了,你如果一定要看代码,那好吧:
		......
		private static final ConcurrentMap<String, ReentrantLock> lockCache = new ConcurrentHashMap<String, ReentrantLock>();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁,不同的KEY,使用不同的锁
			final ReentrantLock lock = new ReentrantLock();
			ReentrantLock existLock = lockCache.putIfAbsent(cacheKey, lock);
			if(existLock!=null){
				lock = existLock;
			}
			try{
				//如果其它线程已经获取同一个KEY的锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

这里面为每个KEY分配一个锁,我不明白你说的“不现实”指哪方面? 还请指教。


神啊!你真觉得你这就是对单个key加锁了?请问你操作ConcurrentHashMap的 putIfAbsent锁在干嘛?而且后面的remove锁在干嘛?你这里的消耗就和楼主的程序一样了,而且,你除了要用ConcurrentHashMap的锁外,还要加锁,你再看看楼主的程序,不尽简单,而且还性能好(没有第二个锁)。
31 楼 dzxiang 2013-07-09  
406657836 写道
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
晕,好像还是有问题,看样子不能太随意了,再发一个吧:
		......
		private static final ReentrantLock lock = new ReentrantLock();  
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			lock.lock();
			try{
				list = (List<Long>)memcached.get(cacheKey);  			
				if(list == null){	
			        //memcache 已经失效或者不存在,去查询数据库  
			        list = getFromDB(param1,param2);  
			        memcached.set(list,5*60);  
				}
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list; 


之前发的那两个完全是坑。这一个也不高效,锁竞争太激烈了(相比ConcurrentHashMap).不过你这个节约了内存开销,通常情况下还是不推荐。更喜欢楼主的ConcurrentHashMap,用空间换时间!

这种做法对锁的竞争只发生于 list == null 的情况下,谈不上竞争多激烈吧? 把代码格式化一下,是不是更清楚一点。

补充:当getFromDB方法(初始化缓存数据)执行时间太长的时候,如果这时候并发的请求非常多,会出现“406657836”所说的竞争激烈的情况,但也可以通过设定超时来解决,代码如下:
		......
		private static final ReentrantLock lock = new ReentrantLock();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁
			try{
				//如果其它线程已经获取锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

回复这个贴子的目的就是想和大家探讨一下这种简单的做法是否可以代替楼主提出的方案,Java Concurrency in Practice的5.6章的例子我也看过,肯定是可行的,但我个人觉得有更简单的解决方案,不用搞的这么复杂。
当然,在实际的项目中,初始化缓存的对象如果比较耗时,我通常在系统启动时用独立的FutureTask加载这个缓存数据,而不是在使用的时候才去检测加载。


可能你没有明白我说的 锁竞争激烈的意思。ConcurrentHashMap 是支持多线程并发写入的,只要key不碰撞到同一segment就不会出现锁竞争。也就是说同时多个线程在写ConcurrentHashMap,而您的这个实现,每个线程都要获得锁才能够写入。都是去竞争的同一把锁,所以锁的竞争更激烈些。楼主是用的默认的ConcurrentHashMap构造函数,最好的情况可以支持16个线程并发写(默认segment为16)。

嗯,简单说:就是使用ConcurrentHashMap尽量让不同的KEY使用不同的锁,每个线程的写入肯定是要获得锁才能写入的,但是它们可以使用不同的锁避免竞争。(每人一个小柜子,各有各的锁,它们可以同时开锁,而不是共用一个大柜子,共用一把锁,需要排除等待)。
很显然,我给出的代码现在主要的问题就是:所有的KEY使用的是同一把锁,当不同的KEY需要写入缓存时,竞争过于激烈,如果改成ConcurrentHashMap存储不同KEY的ReentrantLock,是否可以达到楼主同样的效果?

备注部分有笔误,更正一下:
每个人(KEY)一个小柜子,各有各的锁,需要放东西时,不同的人(KEY)可以同时开锁,而不是共用一个大柜子,共用同一把锁,每个人(KEY)放东西时都需要排队等待同一把锁。


你还是没有明白我的意思!你的lock是一个全局的,也就是所有的key都要竞争这个锁才能去操作memcache,至于你说的每个key各有个的锁?你怎么去为每个key分配的锁?没有看到,也不现实!

你根本没有仔细看过我的回复。

好吧!会有人懂的!

我以为回复的够清楚了,你如果一定要看代码,那好吧:
		......
		private static final ConcurrentMap<String, ReentrantLock> lockCache = new ConcurrentHashMap<String, ReentrantLock>();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁,不同的KEY,使用不同的锁
			final ReentrantLock lock = new ReentrantLock();
			ReentrantLock existLock = lockCache.putIfAbsent(cacheKey, lock);
			if(existLock!=null){
				lock = existLock;
			}
			try{
				//如果其它线程已经获取同一个KEY的锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

这里面为每个KEY分配一个锁,我不明白你说的“不现实”指哪方面? 还请指教。
30 楼 406657836 2013-07-09  
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
晕,好像还是有问题,看样子不能太随意了,再发一个吧:
		......
		private static final ReentrantLock lock = new ReentrantLock();  
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			lock.lock();
			try{
				list = (List<Long>)memcached.get(cacheKey);  			
				if(list == null){	
			        //memcache 已经失效或者不存在,去查询数据库  
			        list = getFromDB(param1,param2);  
			        memcached.set(list,5*60);  
				}
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list; 


之前发的那两个完全是坑。这一个也不高效,锁竞争太激烈了(相比ConcurrentHashMap).不过你这个节约了内存开销,通常情况下还是不推荐。更喜欢楼主的ConcurrentHashMap,用空间换时间!

这种做法对锁的竞争只发生于 list == null 的情况下,谈不上竞争多激烈吧? 把代码格式化一下,是不是更清楚一点。

补充:当getFromDB方法(初始化缓存数据)执行时间太长的时候,如果这时候并发的请求非常多,会出现“406657836”所说的竞争激烈的情况,但也可以通过设定超时来解决,代码如下:
		......
		private static final ReentrantLock lock = new ReentrantLock();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁
			try{
				//如果其它线程已经获取锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

回复这个贴子的目的就是想和大家探讨一下这种简单的做法是否可以代替楼主提出的方案,Java Concurrency in Practice的5.6章的例子我也看过,肯定是可行的,但我个人觉得有更简单的解决方案,不用搞的这么复杂。
当然,在实际的项目中,初始化缓存的对象如果比较耗时,我通常在系统启动时用独立的FutureTask加载这个缓存数据,而不是在使用的时候才去检测加载。


可能你没有明白我说的 锁竞争激烈的意思。ConcurrentHashMap 是支持多线程并发写入的,只要key不碰撞到同一segment就不会出现锁竞争。也就是说同时多个线程在写ConcurrentHashMap,而您的这个实现,每个线程都要获得锁才能够写入。都是去竞争的同一把锁,所以锁的竞争更激烈些。楼主是用的默认的ConcurrentHashMap构造函数,最好的情况可以支持16个线程并发写(默认segment为16)。

嗯,简单说:就是使用ConcurrentHashMap尽量让不同的KEY使用不同的锁,每个线程的写入肯定是要获得锁才能写入的,但是它们可以使用不同的锁避免竞争。(每人一个小柜子,各有各的锁,它们可以同时开锁,而不是共用一个大柜子,共用一把锁,需要排除等待)。
很显然,我给出的代码现在主要的问题就是:所有的KEY使用的是同一把锁,当不同的KEY需要写入缓存时,竞争过于激烈,如果改成ConcurrentHashMap存储不同KEY的ReentrantLock,是否可以达到楼主同样的效果?

备注部分有笔误,更正一下:
每个人(KEY)一个小柜子,各有各的锁,需要放东西时,不同的人(KEY)可以同时开锁,而不是共用一个大柜子,共用同一把锁,每个人(KEY)放东西时都需要排队等待同一把锁。


你还是没有明白我的意思!你的lock是一个全局的,也就是所有的key都要竞争这个锁才能去操作memcache,至于你说的每个key各有个的锁?你怎么去为每个key分配的锁?没有看到,也不现实!

你根本没有仔细看过我的回复。

好吧!会有人懂的!
29 楼 dzxiang 2013-07-09  
406657836 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
晕,好像还是有问题,看样子不能太随意了,再发一个吧:
		......
		private static final ReentrantLock lock = new ReentrantLock();  
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			lock.lock();
			try{
				list = (List<Long>)memcached.get(cacheKey);  			
				if(list == null){	
			        //memcache 已经失效或者不存在,去查询数据库  
			        list = getFromDB(param1,param2);  
			        memcached.set(list,5*60);  
				}
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list; 


之前发的那两个完全是坑。这一个也不高效,锁竞争太激烈了(相比ConcurrentHashMap).不过你这个节约了内存开销,通常情况下还是不推荐。更喜欢楼主的ConcurrentHashMap,用空间换时间!

这种做法对锁的竞争只发生于 list == null 的情况下,谈不上竞争多激烈吧? 把代码格式化一下,是不是更清楚一点。

补充:当getFromDB方法(初始化缓存数据)执行时间太长的时候,如果这时候并发的请求非常多,会出现“406657836”所说的竞争激烈的情况,但也可以通过设定超时来解决,代码如下:
		......
		private static final ReentrantLock lock = new ReentrantLock();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁
			try{
				//如果其它线程已经获取锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

回复这个贴子的目的就是想和大家探讨一下这种简单的做法是否可以代替楼主提出的方案,Java Concurrency in Practice的5.6章的例子我也看过,肯定是可行的,但我个人觉得有更简单的解决方案,不用搞的这么复杂。
当然,在实际的项目中,初始化缓存的对象如果比较耗时,我通常在系统启动时用独立的FutureTask加载这个缓存数据,而不是在使用的时候才去检测加载。


可能你没有明白我说的 锁竞争激烈的意思。ConcurrentHashMap 是支持多线程并发写入的,只要key不碰撞到同一segment就不会出现锁竞争。也就是说同时多个线程在写ConcurrentHashMap,而您的这个实现,每个线程都要获得锁才能够写入。都是去竞争的同一把锁,所以锁的竞争更激烈些。楼主是用的默认的ConcurrentHashMap构造函数,最好的情况可以支持16个线程并发写(默认segment为16)。

嗯,简单说:就是使用ConcurrentHashMap尽量让不同的KEY使用不同的锁,每个线程的写入肯定是要获得锁才能写入的,但是它们可以使用不同的锁避免竞争。(每人一个小柜子,各有各的锁,它们可以同时开锁,而不是共用一个大柜子,共用一把锁,需要排除等待)。
很显然,我给出的代码现在主要的问题就是:所有的KEY使用的是同一把锁,当不同的KEY需要写入缓存时,竞争过于激烈,如果改成ConcurrentHashMap存储不同KEY的ReentrantLock,是否可以达到楼主同样的效果?

备注部分有笔误,更正一下:
每个人(KEY)一个小柜子,各有各的锁,需要放东西时,不同的人(KEY)可以同时开锁,而不是共用一个大柜子,共用同一把锁,每个人(KEY)放东西时都需要排队等待同一把锁。


你还是没有明白我的意思!你的lock是一个全局的,也就是所有的key都要竞争这个锁才能去操作memcache,至于你说的每个key各有个的锁?你怎么去为每个key分配的锁?没有看到,也不现实!

你根本没有仔细看过我的回复。
28 楼 406657836 2013-07-09  
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
晕,好像还是有问题,看样子不能太随意了,再发一个吧:
		......
		private static final ReentrantLock lock = new ReentrantLock();  
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			lock.lock();
			try{
				list = (List<Long>)memcached.get(cacheKey);  			
				if(list == null){	
			        //memcache 已经失效或者不存在,去查询数据库  
			        list = getFromDB(param1,param2);  
			        memcached.set(list,5*60);  
				}
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list; 


之前发的那两个完全是坑。这一个也不高效,锁竞争太激烈了(相比ConcurrentHashMap).不过你这个节约了内存开销,通常情况下还是不推荐。更喜欢楼主的ConcurrentHashMap,用空间换时间!

这种做法对锁的竞争只发生于 list == null 的情况下,谈不上竞争多激烈吧? 把代码格式化一下,是不是更清楚一点。

补充:当getFromDB方法(初始化缓存数据)执行时间太长的时候,如果这时候并发的请求非常多,会出现“406657836”所说的竞争激烈的情况,但也可以通过设定超时来解决,代码如下:
		......
		private static final ReentrantLock lock = new ReentrantLock();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁
			try{
				//如果其它线程已经获取锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

回复这个贴子的目的就是想和大家探讨一下这种简单的做法是否可以代替楼主提出的方案,Java Concurrency in Practice的5.6章的例子我也看过,肯定是可行的,但我个人觉得有更简单的解决方案,不用搞的这么复杂。
当然,在实际的项目中,初始化缓存的对象如果比较耗时,我通常在系统启动时用独立的FutureTask加载这个缓存数据,而不是在使用的时候才去检测加载。


可能你没有明白我说的 锁竞争激烈的意思。ConcurrentHashMap 是支持多线程并发写入的,只要key不碰撞到同一segment就不会出现锁竞争。也就是说同时多个线程在写ConcurrentHashMap,而您的这个实现,每个线程都要获得锁才能够写入。都是去竞争的同一把锁,所以锁的竞争更激烈些。楼主是用的默认的ConcurrentHashMap构造函数,最好的情况可以支持16个线程并发写(默认segment为16)。

嗯,简单说:就是使用ConcurrentHashMap尽量让不同的KEY使用不同的锁,每个线程的写入肯定是要获得锁才能写入的,但是它们可以使用不同的锁避免竞争。(每人一个小柜子,各有各的锁,它们可以同时开锁,而不是共用一个大柜子,共用一把锁,需要排除等待)。
很显然,我给出的代码现在主要的问题就是:所有的KEY使用的是同一把锁,当不同的KEY需要写入缓存时,竞争过于激烈,如果改成ConcurrentHashMap存储不同KEY的ReentrantLock,是否可以达到楼主同样的效果?

备注部分有笔误,更正一下:
每个人(KEY)一个小柜子,各有各的锁,需要放东西时,不同的人(KEY)可以同时开锁,而不是共用一个大柜子,共用同一把锁,每个人(KEY)放东西时都需要排队等待同一把锁。


你还是没有明白我的意思!你的lock是一个全局的,也就是所有的key都要竞争这个锁才能去操作memcache,至于你说的每个key各有个的锁?你怎么去为每个key分配的锁?没有看到,也不现实!
27 楼 dzxiang 2013-07-09  
dzxiang 写道
406657836 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
晕,好像还是有问题,看样子不能太随意了,再发一个吧:
		......
		private static final ReentrantLock lock = new ReentrantLock();  
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			lock.lock();
			try{
				list = (List<Long>)memcached.get(cacheKey);  			
				if(list == null){	
			        //memcache 已经失效或者不存在,去查询数据库  
			        list = getFromDB(param1,param2);  
			        memcached.set(list,5*60);  
				}
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list; 


之前发的那两个完全是坑。这一个也不高效,锁竞争太激烈了(相比ConcurrentHashMap).不过你这个节约了内存开销,通常情况下还是不推荐。更喜欢楼主的ConcurrentHashMap,用空间换时间!

这种做法对锁的竞争只发生于 list == null 的情况下,谈不上竞争多激烈吧? 把代码格式化一下,是不是更清楚一点。

补充:当getFromDB方法(初始化缓存数据)执行时间太长的时候,如果这时候并发的请求非常多,会出现“406657836”所说的竞争激烈的情况,但也可以通过设定超时来解决,代码如下:
		......
		private static final ReentrantLock lock = new ReentrantLock();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁
			try{
				//如果其它线程已经获取锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

回复这个贴子的目的就是想和大家探讨一下这种简单的做法是否可以代替楼主提出的方案,Java Concurrency in Practice的5.6章的例子我也看过,肯定是可行的,但我个人觉得有更简单的解决方案,不用搞的这么复杂。
当然,在实际的项目中,初始化缓存的对象如果比较耗时,我通常在系统启动时用独立的FutureTask加载这个缓存数据,而不是在使用的时候才去检测加载。


可能你没有明白我说的 锁竞争激烈的意思。ConcurrentHashMap 是支持多线程并发写入的,只要key不碰撞到同一segment就不会出现锁竞争。也就是说同时多个线程在写ConcurrentHashMap,而您的这个实现,每个线程都要获得锁才能够写入。都是去竞争的同一把锁,所以锁的竞争更激烈些。楼主是用的默认的ConcurrentHashMap构造函数,最好的情况可以支持16个线程并发写(默认segment为16)。

嗯,简单说:就是使用ConcurrentHashMap尽量让不同的KEY使用不同的锁,每个线程的写入肯定是要获得锁才能写入的,但是它们可以使用不同的锁避免竞争。(每人一个小柜子,各有各的锁,它们可以同时开锁,而不是共用一个大柜子,共用一把锁,需要排除等待)。
很显然,我给出的代码现在主要的问题就是:所有的KEY使用的是同一把锁,当不同的KEY需要写入缓存时,竞争过于激烈,如果改成ConcurrentHashMap存储不同KEY的ReentrantLock,是否可以达到楼主同样的效果?

备注部分有笔误,更正一下:
每个人(KEY)一个小柜子,各有各的锁,需要放东西时,不同的人(KEY)可以同时开锁,而不是共用一个大柜子,共用同一把锁,每个人(KEY)放东西时都需要排队等待同一把锁。
26 楼 dzxiang 2013-07-09  
406657836 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
晕,好像还是有问题,看样子不能太随意了,再发一个吧:
		......
		private static final ReentrantLock lock = new ReentrantLock();  
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			lock.lock();
			try{
				list = (List<Long>)memcached.get(cacheKey);  			
				if(list == null){	
			        //memcache 已经失效或者不存在,去查询数据库  
			        list = getFromDB(param1,param2);  
			        memcached.set(list,5*60);  
				}
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list; 


之前发的那两个完全是坑。这一个也不高效,锁竞争太激烈了(相比ConcurrentHashMap).不过你这个节约了内存开销,通常情况下还是不推荐。更喜欢楼主的ConcurrentHashMap,用空间换时间!

这种做法对锁的竞争只发生于 list == null 的情况下,谈不上竞争多激烈吧? 把代码格式化一下,是不是更清楚一点。

补充:当getFromDB方法(初始化缓存数据)执行时间太长的时候,如果这时候并发的请求非常多,会出现“406657836”所说的竞争激烈的情况,但也可以通过设定超时来解决,代码如下:
		......
		private static final ReentrantLock lock = new ReentrantLock();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁
			try{
				//如果其它线程已经获取锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

回复这个贴子的目的就是想和大家探讨一下这种简单的做法是否可以代替楼主提出的方案,Java Concurrency in Practice的5.6章的例子我也看过,肯定是可行的,但我个人觉得有更简单的解决方案,不用搞的这么复杂。
当然,在实际的项目中,初始化缓存的对象如果比较耗时,我通常在系统启动时用独立的FutureTask加载这个缓存数据,而不是在使用的时候才去检测加载。


可能你没有明白我说的 锁竞争激烈的意思。ConcurrentHashMap 是支持多线程并发写入的,只要key不碰撞到同一segment就不会出现锁竞争。也就是说同时多个线程在写ConcurrentHashMap,而您的这个实现,每个线程都要获得锁才能够写入。都是去竞争的同一把锁,所以锁的竞争更激烈些。楼主是用的默认的ConcurrentHashMap构造函数,最好的情况可以支持16个线程并发写(默认segment为16)。

嗯,简单说:就是使用ConcurrentHashMap尽量让不同的KEY使用不同的锁,每个线程的写入肯定是要获得锁才能写入的,但是它们可以使用不同的锁避免竞争。(每人一个小柜子,各有各的锁,它们可以同时开锁,而不是共用一个大柜子,共用一把锁,需要排除等待)。
很显然,我给出的代码现在主要的问题就是:所有的KEY使用的是同一把锁,当不同的KEY需要写入缓存时,竞争过于激烈,如果改成ConcurrentHashMap存储不同KEY的ReentrantLock,是否可以达到楼主同样的效果?
25 楼 406657836 2013-07-08  
dzxiang 写道
dzxiang 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
晕,好像还是有问题,看样子不能太随意了,再发一个吧:
		......
		private static final ReentrantLock lock = new ReentrantLock();  
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			lock.lock();
			try{
				list = (List<Long>)memcached.get(cacheKey);  			
				if(list == null){	
			        //memcache 已经失效或者不存在,去查询数据库  
			        list = getFromDB(param1,param2);  
			        memcached.set(list,5*60);  
				}
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list; 


之前发的那两个完全是坑。这一个也不高效,锁竞争太激烈了(相比ConcurrentHashMap).不过你这个节约了内存开销,通常情况下还是不推荐。更喜欢楼主的ConcurrentHashMap,用空间换时间!

这种做法对锁的竞争只发生于 list == null 的情况下,谈不上竞争多激烈吧? 把代码格式化一下,是不是更清楚一点。

补充:当getFromDB方法(初始化缓存数据)执行时间太长的时候,如果这时候并发的请求非常多,会出现“406657836”所说的竞争激烈的情况,但也可以通过设定超时来解决,代码如下:
		......
		private static final ReentrantLock lock = new ReentrantLock();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁
			try{
				//如果其它线程已经获取锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

回复这个贴子的目的就是想和大家探讨一下这种简单的做法是否可以代替楼主提出的方案,Java Concurrency in Practice的5.6章的例子我也看过,肯定是可行的,但我个人觉得有更简单的解决方案,不用搞的这么复杂。
当然,在实际的项目中,初始化缓存的对象如果比较耗时,我通常在系统启动时用独立的FutureTask加载这个缓存数据,而不是在使用的时候才去检测加载。


可能你没有明白我说的 锁竞争激烈的意思。ConcurrentHashMap 是支持多线程并发写入的,只要key不碰撞到同一segment就不会出现锁竞争。也就是说同时多个线程在写ConcurrentHashMap,而您的这个实现,每个线程都要获得锁才能够写入。都是去竞争的同一把锁,所以锁的竞争更激烈些。楼主是用的默认的ConcurrentHashMap构造函数,最好的情况可以支持16个线程并发写(默认segment为16)。
24 楼 dzxiang 2013-07-08  
dzxiang 写道
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
晕,好像还是有问题,看样子不能太随意了,再发一个吧:
		......
		private static final ReentrantLock lock = new ReentrantLock();  
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			lock.lock();
			try{
				list = (List<Long>)memcached.get(cacheKey);  			
				if(list == null){	
			        //memcache 已经失效或者不存在,去查询数据库  
			        list = getFromDB(param1,param2);  
			        memcached.set(list,5*60);  
				}
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list; 


之前发的那两个完全是坑。这一个也不高效,锁竞争太激烈了(相比ConcurrentHashMap).不过你这个节约了内存开销,通常情况下还是不推荐。更喜欢楼主的ConcurrentHashMap,用空间换时间!

这种做法对锁的竞争只发生于 list == null 的情况下,谈不上竞争多激烈吧? 把代码格式化一下,是不是更清楚一点。

补充:当getFromDB方法(初始化缓存数据)执行时间太长的时候,如果这时候并发的请求非常多,会出现“406657836”所说的竞争激烈的情况,但也可以通过设定超时来解决,代码如下:
		......
		private static final ReentrantLock lock = new ReentrantLock();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁
			try{
				//如果其它线程已经获取锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  

回复这个贴子的目的就是想和大家探讨一下这种简单的做法是否可以代替楼主提出的方案,Java Concurrency in Practice的5.6章的例子我也看过,肯定是可行的,但我个人觉得有更简单的解决方案,不用搞的这么复杂。
当然,在实际的项目中,初始化缓存的对象如果比较耗时,我通常在系统启动时用独立的FutureTask加载这个缓存数据,而不是在使用的时候才去检测加载。
23 楼 dzxiang 2013-07-08  
dzxiang 写道
dzxiang 写道
406657836 写道
dzxiang 写道
晕,好像还是有问题,看样子不能太随意了,再发一个吧:
		......
		private static final ReentrantLock lock = new ReentrantLock();  
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			lock.lock();
			try{
				list = (List<Long>)memcached.get(cacheKey);  			
				if(list == null){	
			        //memcache 已经失效或者不存在,去查询数据库  
			        list = getFromDB(param1,param2);  
			        memcached.set(list,5*60);  
				}
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list; 


之前发的那两个完全是坑。这一个也不高效,锁竞争太激烈了(相比ConcurrentHashMap).不过你这个节约了内存开销,通常情况下还是不推荐。更喜欢楼主的ConcurrentHashMap,用空间换时间!

这种做法对锁的竞争只发生于 list == null 的情况下,谈不上竞争多激烈吧? 把代码格式化一下,是不是更清楚一点。

补充:当getFromDB方法(初始化缓存数据)执行时间太长的时候,如果这时候并发的请求非常多,会出现“406657836”所说的竞争激烈的情况,但也可以通过设定超时来解决,代码如下:
		......
		private static final ReentrantLock lock = new ReentrantLock();
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			//只在没有缓存数据的情况下才加锁
			try{
				//如果其它线程已经获取锁,则最多等待10秒钟
				if(lock.tryLock(10, TimeUnit.SECONDS)){
					//正常获取锁,再次检测缓存中的数据是否已被其它线程加载
					list = (List<Long>)memcached.get(cacheKey);  
					if(list == null){	
				        //memcache 已经失效或者不存在,则加载数据
				        list = getFromDB(param1,param2);  
				        memcached.set(list,5*60);  
					}
				}
				//超时,反馈给调用者或打印超时信息
				else{
					//System.out.println("等待超时,数据尚未加载完成.");
					throw new TimeoutException("数据尚未加载完成");
				}				
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list;  
22 楼 dzxiang 2013-07-08  
dzxiang 写道
406657836 写道
dzxiang 写道
晕,好像还是有问题,看样子不能太随意了,再发一个吧:
		......
		private static final ReentrantLock lock = new ReentrantLock();  
		......

		//计算缓存的key  
		String cacheKey = getCacheKey(param1,param2);  
		//查询memcached  
		List<Long> list = (List<Long>)memcached.get(cacheKey);  
		if(list == null){
			lock.lock();
			try{
				list = (List<Long>)memcached.get(cacheKey);  			
				if(list == null){	
			        //memcache 已经失效或者不存在,去查询数据库  
			        list = getFromDB(param1,param2);  
			        memcached.set(list,5*60);  
				}
		    }finaly{
		    	lock.unlock();
		    }				
		}  
		    
		return list; 


之前发的那两个完全是坑。这一个也不高效,锁竞争太激烈了(相比ConcurrentHashMap).不过你这个节约了内存开销,通常情况下还是不推荐。更喜欢楼主的ConcurrentHashMap,用空间换时间!

这种做法对锁的竞争只发生于 list == null 的情况下,谈不上竞争多激烈吧? 把代码格式化一下,是不是更清楚一点。
21 楼 dzxiang 2013-07-08  
406657836 写道
dzxiang 写道
晕,好像还是有问题,看样子不能太随意了,再发一个吧:

......
private static final ReentrantLock lock = new ReentrantLock(); 
......

//计算缓存的key 
String cacheKey = getCacheKey(param1,param2); 
//查询memcached 
List<Long> list = (List<Long>)memcached.get(cacheKey); 
if(list == null){
lock.lock();
try{
list = (List<Long>)memcached.get(cacheKey); 
if(list == null){
        //memcache 已经失效或者不存在,去查询数据库 
        list = getFromDB(param1,param2); 
        memcached.set(list,5*60); 
}
    }finaly{
    lock.unlock();
    }

   
return list; 

之前发的那两个完全是坑。这一个也不高效,锁竞争太激烈了(相比ConcurrentHashMap).不过你这个节约了内存开销,通常情况下还是不推荐。更喜欢楼主的ConcurrentHashMap,用空间换时间!

这种做法对锁的竞争只发生于 list == null 的情况下,谈不上竞争多激烈吧?
20 楼 406657836 2013-07-08  
dzxiang 写道
晕,好像还是有问题,看样子不能太随意了,再发一个吧:

......
private static final ReentrantLock lock = new ReentrantLock(); 
......

//计算缓存的key 
String cacheKey = getCacheKey(param1,param2); 
//查询memcached 
List<Long> list = (List<Long>)memcached.get(cacheKey); 
if(list == null){
lock.lock();
try{
list = (List<Long>)memcached.get(cacheKey); 
if(list == null){
        //memcache 已经失效或者不存在,去查询数据库 
        list = getFromDB(param1,param2); 
        memcached.set(list,5*60); 
}
    }finaly{
    lock.unlock();
    }

   
return list; 

之前发的那两个完全是坑。这一个也不高效,锁竞争太激烈了(相比ConcurrentHashMap).不过你这个节约了内存开销,通常情况下还是不推荐。更喜欢楼主的ConcurrentHashMap,用空间换时间!
19 楼 dzxiang 2013-07-08  
晕,好像还是有问题,看样子不能太随意了,再发一个吧:

......
private static final ReentrantLock lock = new ReentrantLock(); 
......

//计算缓存的key 
String cacheKey = getCacheKey(param1,param2); 
//查询memcached 
List<Long> list = (List<Long>)memcached.get(cacheKey); 
if(list == null){
lock.lock();
try{
list = (List<Long>)memcached.get(cacheKey); 
if(list == null){
        //memcache 已经失效或者不存在,去查询数据库 
        list = getFromDB(param1,param2); 
        memcached.set(list,5*60); 
}
    }finaly{
    lock.unlock();
    }

   
return list; 

相关推荐

    电脑知识\让系统快步如飞正确设置Windows缓存

    ### 电脑知识:让系统快步如飞——正确设置Windows缓存 在计算机科学领域,缓存技术是一种广泛采用的方法,用于提高数据访问速度并减少系统延迟。通过将频繁访问的数据存储在更快的存储介质中(通常是内存),可以...

    SAS缓存-关闭工具(服务器磁盘读取慢可以用此工具)

    1. 缓存策略不当:如果缓存策略设置不合理,如写缓存策略设置为“回写”模式,而在断电或异常关机时,未写入的数据可能会丢失,导致系统性能下降。 2. 缓存容量不足:如果缓存大小不足以应对大量并发的I/O请求,缓存...

    IE缓存读取

    **读取IE缓存的方法** 1. **手动查看**:用户可以在IE浏览器的“工具”菜单中选择“Internet选项”,在“常规”选项卡下的“浏览历史”部分点击“设置”来查看和管理缓存。 2. **编程访问**:开发者可以通过编程方式...

    基于Retrofit2okhttp3的数据缓存cache技术修改了缓存时间能够设置缓存时间了.rar

    此外,还需要确保在无网络连接时,能够正确处理缓存数据的读取。 通过以上步骤,你可以在你的Android应用中实现基于Retrofit2和OkHttp3的数据缓存,并根据需要调整缓存时间。这不仅提高了应用的性能,还能在离线...

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

    此外,缓存的生命周期在IIS默认设置下随着应用程序重启而结束,可以通过IIS管理工具或编程方式配置缓存的持久化。 在实际开发中,应考虑到缓存的大小限制,因为应用程序域中的内存资源有限。如果需要缓存较大的数据...

    Android13 14系统 app获取第三方应用缓存的方法

    总结来说,虽然Android 13和14对第三方应用的缓存访问增加了难度,但通过正确申请权限、使用新的API以及遵循隐私规定,还是可以实现这一功能的。在开发过程中,务必谨慎处理数据,尊重用户隐私,确保应用的合法性与...

    SC超级缓存设置教程和工具

    本教程将详细解释SC超级缓存的配置方法,并提供相应的工具来帮助你更好地管理和利用这个功能。 一、SC超级缓存的基本原理 SC超级缓存的核心是将频繁访问的数据存储在高速缓存中,当用户或系统需要这些数据时,可以...

    SC超级缓存设置和工具.rar

    SC超级缓存是一种高效的数据存储...通过正确设置和使用SC超级缓存,不仅可以减少硬件损耗,还可以显著提升服务质量和用户体验。因此,深入理解和掌握SC超级缓存的原理和配置技巧,对于任何IT管理员来说都是至关重要的。

    Xpath读取xml文件,实现文件缓存。

    9. **测试与调试**: 对于这样的功能,编写单元测试是非常重要的,可以确保XPath表达式的正确性和缓存机制的稳定性。可以使用诸如JUnit(Java)、pytest(Python)或Mocha/Chai(JavaScript)等测试框架进行测试。 ...

    HP 服务器高速缓存的设置

    正确的高速缓存设置能够有效提高数据读写速度,从而改善整体服务器性能。当然,在实际操作过程中还需要注意各种细节,确保设置正确无误。希望本文能为需要对HP服务器进行高速缓存设置的用户提供帮助。

    两次请求相同的一个URL,会产生缓存问题。

    4. **服务器未正确设置缓存指令**:若服务器未正确设置缓存控制头,可能导致浏览器无法正确判断何时应获取新资源。 在开发过程中,开发者需要理解和正确处理这些问题,以确保用户体验的一致性和数据的准确性。工具...

    Android文件缓存与内存缓存

    当用户请求数据时,系统首先查找内存缓存,如果找到则直接返回,否则再从文件缓存中读取,若仍未找到,则从网络或其他源获取,同时将数据存入内存和文件缓存,为后续请求提供服务。 在Android中,对于ListView加载...

    cpu二级缓存设置

    另一种开启CPU二级缓存的方法是在BIOS中进行设置。这种方法适用于大多数计算机,尤其是那些允许用户自定义硬件设置的计算机。 **步骤1**:重启电脑,并在开机过程中按下DEL键(或根据屏幕提示的其他按键),进入...

    java文件读取方法.doc

    在进行文件读取时,需要注意错误处理,如在给定的代码中,使用了`try-catch-finally`块确保资源的正确关闭。此外,为了提高性能,通常会使用缓冲技术,如`BufferedReader`和`BufferedInputStream`,它们可以在内部...

    ssd缓存软件服务器设置缓存的最佳选择

    综上所述,正确设置和使用SSD缓存软件能显著提升服务器性能,但需谨慎选择软件、合理配置参数,并保持对系统状态的密切关注。通过这些方法,企业可以充分发挥SSD的潜力,为业务提供更高效、可靠的存储解决方案。

    Cache 缓存数据和删除缓存的简单示例

    例如,在一个ASP.NET MVC的控制器方法中,可以使用`[OutputCache(Duration = 60)]`注解来设置60秒的缓存期。 Data Cache则主要用于存储应用程序级别的数据,例如数据库查询结果。开发者可以使用`System.Web.Caching...

    php页面缓存方法小结

    该函数通常在输出缓冲结束前被调用,以确保页面内容被正确地写入缓存文件。 这些缓存方法可以显著减少数据库查询次数,并降低服务器负载,尤其适合于对性能要求较高的Web应用。需要注意的是,缓存机制在提高性能的...

    php缓存技术总结

    在PHP中,可以将查询结果存储在文件或专门的缓存系统中,下次请求时直接读取缓存。 四、查询缓存 查询缓存是针对数据库查询结果的缓存,它保存特定查询的数据结果,当下次执行相同查询时,直接从缓存中读取数据,...

Global site tag (gtag.js) - Google Analytics