上周同事做了一个代码的分享,分享了在code中解决并发请求访问cache的问题。
使用了ConcurrentHashMap充当mutex锁,伪代码如下:
public class XXX{ private ConcurrentHashMap map = new ConcurrentHashMap(); public Object get(key){ Object o = cache.get(key); if(o==null){ Object lock = new Object(); /*关于ConcurrentHashMap 的 putIfAbsent方法 doc 是这样说的: * the previous value associated with the specified key, * or null if there was no mapping for the key */ Object _lock = map.putIfAbsent(key, lock); if(_lock==null){//说明没有其他线程同时请求 o = db.get(key); cache.put(key, o); _lock.notifyAll();//唤醒其他等待的线程 }else{ _lock.wait();//线程等待,被唤醒 o = cache.get(key); } } return o; } }
不由的想到了之前看到的Timyang在blog中写的《Memcache mutex设计模式》http://timyang.net/programming/memcache-mutex/,另外还有孙立的一个跟帖文章http://www.cnblogs.com/sunli/archive/2010/07/27/cache_key_mutex.html。今天拿出来又看了一下。基本上和同事的思路是一样的,只是用memcached完成的mutex锁
if (memcache.get(key) == null) { // 3 min timeout to avoid mutex holder crash if (memcache.add(key_mutex, 3 * 60 * 1000) == true) { value = db.get(key); memcache.set(key, value); memcache.delete(key_mutex); } else { sleep(50); retry(); } }
Ps:这里顺便复习一下memcached set,replace,add方法
* add 仅当存储空间中不存在键相同的数据时才保存
* replace 仅当存储空间中存在键相同的数据时才保存
* set 与add和replace不同,无论何时都保存,如果 key 存在就是替換 value
然后想到了用Double Check 的方式应该也是一个不错的选择,并且代码看上去更简洁
public Object get(key){ Object o = cache.get(key); if(o==null){ doGet(key); } return o; } public synchronized Object doGet(key){ Object o = cache.get(key); if(o==null){ o = db.get(key); } return o; }