不使用mutex设计模式解决并发访问cache

上周同事做了一个代码的分享,分享了在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;
}
原文地址:https://www.cnblogs.com/beanchoc/p/3209541.html