溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Java中redis分布式鎖的實(shí)現(xiàn)方法

發(fā)布時(shí)間:2021-07-01 11:18:14 來源:億速云 閱讀:218 作者:chen 欄目:大數(shù)據(jù)

這篇文章主要介紹“Java中redis分布式鎖的實(shí)現(xiàn)方法”,在日常操作中,相信很多人在Java中redis分布式鎖的實(shí)現(xiàn)方法問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Java中redis分布式鎖的實(shí)現(xiàn)方法”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

redis分布式鎖

1.什么是分布式鎖?

分布式鎖是用于解決多個進(jìn)程互斥地訪問共享資源時(shí)產(chǎn)生的問題。

舉個例子,當(dāng)你做新增操作時(shí),為了防止重復(fù)插入,需要進(jìn)行“查找->是否存在->不存在->添加”的邏輯判斷。

如果有兩個線程同時(shí)進(jìn)入這個邏輯判斷,那么兩個線程同時(shí)進(jìn)入“不存在”這個判斷時(shí),就會有兩次插入操作。

一般為了解決這種問題,Java中可以用synchronized等線程安全的方法來解決,但是當(dāng)應(yīng)用是多實(shí)例部署時(shí),就需要用分布式鎖來解決了。

2.分布式鎖有哪些?

1.基于數(shù)據(jù)庫

2.基于redis

3.基于中間件(zookeeper)

3.分布式鎖的使用條件

使用分布式鎖需要滿足以下三個條件

1.互斥。即鎖只能由一個線程獲取。

2.不會死鎖。線程崩潰等原因?qū)е麻L時(shí)間獲取鎖不釋放,要有機(jī)制能自動釋放鎖。

3.不能誤解鎖。加鎖和解鎖必須要是同一個線程。

4.redis分布式鎖的原理

為什么使用redis作為分布式鎖呢?

最主要的原因是redis是單線程訪問的,指令是按隊(duì)列一條條順序執(zhí)行。

我們只需要把上鎖這個操作的指令寫成一個腳本,讓redis執(zhí)行即可。

5.具體步驟

5.1.獲取鎖對象

鎖對象包括兩個值key和requestId。

key即為鎖的key,一般是場景+唯一標(biāo)識

requestId是鎖的值,用于解鎖時(shí)能保證不會誤解鎖,一般是key+時(shí)間戳。

public RedisLockBean getLockBean(String lock, String id){

    String LockKey = lock.concat(id);
    String requestId = LockKey.concat(String.valueOf(System.currentTimeMillis()));

    return new RedisLockBean().setKey(LockKey).setRequestId(requestId);
}
5.2.加鎖

使用代碼執(zhí)行命令

SET key requestId NX PX 10000

NX: 如果不存在就設(shè)置

PX milliseconds: 鍵的過期時(shí)間設(shè)置為多少毫秒

private static final String SET_IF_ABSENT = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
private Long expireTime = 10000L;

public Boolean setLock(RedisLockBean lockBean){

    RedisCallback<String> stringRedisCallback = (connection) ->{
        JedisCommands commands  = (JedisCommands) connection.getNativeConnection();
        return commands.set(lockBean.getKey(), lockBean.getRequestId(), SET_IF_ABSENT, SET_WITH_EXPIRE_TIME, expireTime);
    };

    String result = (String) redisTemplate.execute(stringRedisCallback);
    return !StringUtils.isEmpty(result);
}
5.3.判斷是否獲取到鎖,如果沒有獲取到,可循環(huán)獲取,或拋出異常。
5.4.解鎖

執(zhí)行命令

if redis.call("get",KEYS[1]) == ARGV[1]
then
    return redis.call("del", KEYS[1])
else
    return 0
end
private static final String UNLOCK_LUA;

static {
    StringBuilder sb = new StringBuilder();
    sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
    sb.append("then ");
    sb.append("    return redis.call(\"del\",KEYS[1]) ");
    sb.append("else ");
    sb.append("    return 0 ");
    sb.append("end ");
    UNLOCK_LUA = sb.toString();
}

public Boolean releaseLock(RedisLockBean lockBean){
    // 釋放鎖的時(shí)候,有可能因?yàn)槌宙i之后方法執(zhí)行時(shí)間大于鎖的有效期,此時(shí)有可能已經(jīng)被另外一個線程持有鎖,所以不能直接刪除
    try {
        List<String> keys = new ArrayList<>();
        keys.add(lockBean.getKey());
        List<String> args = new ArrayList<>();
        args.add(lockBean.getRequestId());
    
        // 使用lua腳本刪除redis中匹配value的key,可以避免由于方法執(zhí)行時(shí)間過長而redis鎖自動過期失效的時(shí)候誤刪其他線程的鎖
        // spring自帶的執(zhí)行腳本方法中,集群模式直接拋出不支持執(zhí)行腳本的異常,所以只能拿到原redis的connection來執(zhí)行腳本
        RedisCallback<Long> callback = (connection) -> {
            Object nativeConnection = connection.getNativeConnection();
            // 集群模式和單機(jī)模式雖然執(zhí)行腳本的方法一樣,但是沒有共同的接口,所以只能分開執(zhí)行
            // 集群模式
            if (nativeConnection instanceof JedisCluster) {
                return (Long) ((JedisCluster) nativeConnection).eval(UNLOCK_LUA, keys, args);
            }
    
            // 單機(jī)模式
            else if (nativeConnection instanceof JedisCommands) {
                return (Long) ((Jedis) nativeConnection).eval(UNLOCK_LUA, keys, args);
            }
            return 0L;
        };
        Long result = (Long) redisTemplate.execute(callback);
    
        return result != null && result > 0;
    } catch (Exception e) {
        log.error("release lock occured an exception", e);
    }
    return false;
}

到此,關(guān)于“Java中redis分布式鎖的實(shí)現(xiàn)方法”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI