缓存击穿解决途径——二级缓存+互斥锁 Java实现

首先是互斥锁。
优点:能够根据请求的key加锁,提高了并发性。
缺点:GC不友好,不是分布式的。

public final class MutexLock {

    private static final ConcurrentHashMap<String,CountDownLatch> locks=new ConcurrentHashMap<>();

    public static boolean tryLock(String key){
        //能过滤掉后持有相同key的线程
        return !locks.containsKey(key);
    }

    public static void lock(String key){
        //多线程持有相同的key并发执行时,可能同时调用 {@code lock(key)},但只有一个线程能成功
        //并发时阻塞的线程只能等着,其他持有相同key的线程可能通过{@code tryLock(key)}判断后去做其他的事情
        //对GC不友好,因为会new很多CountDownLatch,但不会超多最大线程数。
        while(locks.putIfAbsent(key,new CountDownLatch(1))!=null){
            CountDownLatch lock=locks.get(key);
            try {
                lock.await(50, TimeUnit.MILLISECONDS);
            }catch (InterruptedException e){
                //处理异常
            }
        }
    }

    public static void unLock(String key){
       CountDownLatch lock=locks.remove(key);
       lock.countDown();
    }

}

借助互斥锁解决缓存击穿问题:

 public String getVal(String key){
        //一级缓存,取到直接返回
        String val=getValFromRedis(key);
        if(getValFromRedis(key)==null){
            if(MutexLock.tryLock(key)){
                try {
                    MutexLock.lock(key);
                    val=getValFromRedis(key);
                    if (val==null){
                        val=getValFromMysql(key);
                    }
                     //可以打日志看一下更新的值
                     if (val!=null){
                         updateCache(key,val);
                    }
                }finally {
                    MutexLock.unLock(key);
                }
            }else{
                //二级缓存取
                val=getValFromHbase(key);
                //还取不到,重试
                if(val==null){
                    //重试次数可以配在zk里面
                    int i=3;
                    while(i>0){
                        val=getVal(key);
                        if(val!=null){
                            break;
                        }
                        i--;
                    }
                    //取不到,重试还是取不到
                    if(val==null){
                        //发报警邮件,或记录在什么地方
                    }
                }
            }
        }
        return val;
    }

你可能感兴趣的:(java,并发)