使用redis 處理高併發場景

1.原理: 當同一個用戶獲取鎖以後,會讓該用戶一直持有鎖。一樣 的用戶再次獲取,會根據原子性 ,lock返回true。redis

 

 /**
     * 獲取鎖(非公平鎖), 默認獲取超時爲2分鐘
     */
    public boolean lock(){
        return lock(GETLOCK_TIMEOUT/1000);
    }
    /**
     * 獲取鎖(非公平鎖), 獲取超時爲timeoutSeconds秒
     */
    public boolean lock(int timeoutSeconds){
        int timeout = timeoutSeconds*1000;
        while (timeout >= 0) {
            //在同一個JVM內,防止出現如下狀況:當已經獲取此鎖的線程實際業務運行時間超過LOCK_EXPIRE_TIMEOUT時,此鎖會再被其它線程獲取
            if(LOCKED_NAMES.contains(lockname)){
                int sleeptime = random.nextInt(100)+100;
                try{Thread.sleep(sleeptime);} catch (InterruptedException e) {}
                timeout -= sleeptime;
                continue;
            }
            //當分佈式程序獲取同一個鎖時,防止出現如下狀況:當已經獲取此鎖的進程(物理機器)實際業務運行時間超過LOCK_EXPIRE_TIMEOUT時,此鎖會再被其它進程(物理機器)獲取
            String hearbeatFlag = RedisCacheUtil.get(lockname + HEARTBEAT_SUFFIX , String.class );
            if(hearbeatFlag!=null){
                int sleeptime = random.nextInt(100)+100;
                try{Thread.sleep(sleeptime);} catch (InterruptedException e) {}
                timeout -= sleeptime;
                continue;
            }
            
            //鎖到期時間
            long expires = System.currentTimeMillis() + LOCK_EXPIRE_TIMEOUT + 1;
            boolean ok = setNX(expires);
            if (ok) {
                //System.out.println(Thread.currentThread().getName()+":得到鎖成功,經過setNX");
                LOCKED_NAMES.add(lockname);
                locked = true;
                return locked;
            }
            //獲取當前鎖信息
            RedisLockValue redisLockValue = get();
            if (redisLockValue != null && redisLockValue.getExpireTime() < System.currentTimeMillis()) {
                //利用getSet的原子性操做來設置並獲取到舊值
                RedisLockValue oldRedisLockValue = getSet(expires);
                //最早設置的獲取鎖
                if (oldRedisLockValue != null && oldRedisLockValue.getToken().equals(redisLockValue.getToken())) {
                    //System.out.println(Thread.currentThread().getName()+":得到鎖成功,經過getSet");
                    LOCKED_NAMES.add(lockname);
                    locked = true;
                    return locked;
                }
            }
            //防止飢餓線程出現 採用隨機休眠時間
            int sleeptime = random.nextInt(100)+100;
            try{Thread.sleep(sleeptime);} catch (InterruptedException e) {}
            timeout -= sleeptime;
        }
        
        //若是已經超時,但只是由於此時緩存還有值,由於反序列化異常致使GET取不到時,解決死鎖問題
        try{Thread.sleep(300);} catch (InterruptedException e) {}
        RedisLockValue redisLockValue = get();
        if(redisLockValue==null){
            //強制刪掉便可
            LOCKED_NAMES.remove(lockname);
            RedisCacheUtil.delete(lockname);
            RedisCacheUtil.delete( lockname + HEARTBEAT_SUFFIX );
        }
        return locked;
    }
相關文章
相關標籤/搜索