bl双性强迫侵犯h_国产在线观看人成激情视频_蜜芽188_被诱拐的少孩全彩啪啪漫画

Redis中怎樣實(shí)現(xiàn)分布式鎖

這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)redis中怎樣實(shí)現(xiàn)分布式鎖,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

創(chuàng)新互聯(lián)建站主要從事網(wǎng)站建設(shè)、網(wǎng)站制作、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)豐城,十年網(wǎng)站建設(shè)經(jīng)驗(yàn),價格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):13518219792

在Java里面,如果多個線程同時訪問公共資源,不做些同步措施可能就會對數(shù)據(jù)的一致性造成破壞,例如下面的例子,多個線程同時對COUNT做 +1操作(為了測試方便就使用單機(jī)多個線程執(zhí)行)

import lombok.SneakyThrows;import java.util.concurrent.Semaphore;public class ThreadLockTest extends Thread{// 多個線程對COUNT進(jìn)行操作    public static volatile int COUNT = 0;    // 信號燈,用于所有線程執(zhí)行完后主線程輸出    public static Semaphore semaphore = new Semaphore(0);    @SneakyThrows    @Override    public void run() {for (int i = 0; i < 10; i++) {
            System.out.println("線程:" + Thread.currentThread().getName() + " 操作:" + COUNT++);            Thread.sleep(100);        }try {
            System.out.println("線程:" + Thread.currentThread().getName() + " 執(zhí)行完成");            semaphore.release();        } catch (Exception e) {
            e.printStackTrace();        }
    }public static void main(String[] args) throws InterruptedException {new ThreadLockTest().start();        new ThreadLockTest().start();        new ThreadLockTest().start();        semaphore.acquire(3);        System.out.println("主線程輸入總數(shù):" + COUNT);    }
}

3個線程,每個線程循環(huán)執(zhí)行 "i+1" 10次,預(yù)期輸出結(jié)果應(yīng)該是30,輸出結(jié)果不一定是30,這是因?yàn)闆]同步處理,對一致性造成破壞

Redis中怎樣實(shí)現(xiàn)分布式鎖Redis中怎樣實(shí)現(xiàn)分布式鎖

不做同步處理會造成結(jié)果與我們期望的不一樣,所以需要用到synchronized關(guān)鍵字或者Lock對象來做同步處理,但是這兩者都只適用于單機(jī)的服務(wù)器。在分布式服務(wù)器下,無法使用synchronized關(guān)鍵字和Lock對象進(jìn)行同步操作,這個時候就要想想其他辦法,Redis就提供了一些分布式鎖的方案。如下:

Redis提供呢NX函數(shù)給我們設(shè)置鎖,就是向Redis插入一個key-value,key就是鎖名稱,value可以使用UUID或者 機(jī)器號+線程名稱,當(dāng)用完的時候再刪除這個key,但是只有value一致的情況下才能刪除,這樣才能保證加鎖與解鎖的線程是同一個,SETNX是Redis提供的一個命令,當(dāng)Redis中不存在指定的key時才能成功插入,Jedis也有對應(yīng)的函數(shù)。

public static long lock(String key, String requestId){return jedis.setnx(key, requestId);    // 獲取鎖,返回1則獲取鎖成功,返回0則獲取鎖失敗}

這里順便把解鎖的函數(shù)貼出來,這里使用到lua腳本直接判斷刪除。如果不用lua腳本刪除我們的程序要分成幾部: 1.根據(jù)key查詢值;2.判斷值與當(dāng)前線程值是否一致;3.刪除key。但是這幾部走下來,如果中間出了什么問題就會造成一些麻煩(此處下面再展開可能會有哪些麻煩)。所以為了保證原子性,用lua一步到位。

//刪除key的lua腳本,先比較requestId是否相等,相等則刪除,使用腳本執(zhí)行保證原子性private static final String DEL_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";public static long unLock(String key, String requestId) {//刪除成功表示解鎖成功,返回1則解鎖成功,返回0則解鎖失敗    return jedis.eval(DEL_SCRIPT, Collections.singletonList(key), Collections.singletonList(requestId));}

再執(zhí)行一下代碼加上鎖之后的執(zhí)行結(jié)果

@SneakyThrows@Overridepublic void run() {for (int i = 0; i < 10; i++) {
        String uuid = UUID.randomUUID().toString();        while(1 != JedisTest.lock("COUNT_ADD_LOCK", uuid));        System.out.println("線程:" + Thread.currentThread().getName() + " 操作:" + COUNT++);        Thread.sleep(100);        JedisTest.unLock("COUNT_ADD_LOCK", uuid);    }try {
        System.out.println("線程:" + Thread.currentThread().getName() + " 執(zhí)行完成");        semaphore.release();    } catch (Exception e) {
        e.printStackTrace();    }
}

執(zhí)行結(jié)果也每次都能保證輸出的是我們的預(yù)期值

Redis中怎樣實(shí)現(xiàn)分布式鎖

上面的加鎖方案會有個問題,就是當(dāng)加鎖的那個線程所在的服務(wù)器宕機(jī)之后,鎖就一致存在,會導(dǎo)致其他線程一直在阻塞,所以需要加上一個失效時間。當(dāng)加鎖的服務(wù)器宕機(jī)之后過了過了有效時間其他服務(wù)器就能再上鎖。

加失效時間

private static final int LOCK_EX_SECONDS = 5;public static long lock(String key, String requestId){return jedis.setex(key,LOCK_EX_SECONDS,requestId);}

這里往Redis插入數(shù)據(jù)的時候必須同時加上過期時間,如果分成兩步走:
    1.插入數(shù)據(jù),
    2.給key設(shè)置失效時間 
有可能在第一步插入數(shù)據(jù)之后服務(wù)器宕機(jī)了,這樣就造成失效時間沒有設(shè)置上,死鎖的問題就又出現(xiàn)了,所以必須插入數(shù)據(jù)的時候同時設(shè)置失效時間。

給key加了失效時間比沒有好,但是缺陷還是有很多,因?yàn)槭r間都是根據(jù)我們開發(fā)人員評估這段業(yè)務(wù)執(zhí)行起來需要多少時間,并不是準(zhǔn)確計(jì)算每次請求這段業(yè)務(wù)所需的時間。這樣就會造成一個問題:我們業(yè)務(wù)還沒執(zhí)行完,鎖因?yàn)闀r間到期而被釋放,再有其他線程同時訪問到這部分公共資源,也會破壞其一致性。還有一個就是上面提到解鎖操作那3步(1.根據(jù)key查詢值;2.判斷值與當(dāng)前線程值是否一致;3.刪除key)不能分開執(zhí)行的原因,例如:
    線程A:lock(),插入數(shù)據(jù)并且設(shè)置過期時間
    線程A:執(zhí)行業(yè)務(wù)完畢
    線程A:根據(jù)鎖的key獲取value--解鎖第一步
    線程A:判斷當(dāng)前線程的value與鎖的value一致--解鎖第二部
        上面判斷結(jié)果一致情況下,key這個時候剛好過了有效時間
    線程B:lock(),獲取鎖成功
    線程A:unlock() 解鎖成功,因?yàn)樯厦嬉呀?jīng)判斷了value一致,所以只需要del(key)就能解鎖
從上面這個流程看到,如果解鎖不保證原子性可能會出現(xiàn)線程A把線程B的鎖釋放的問題。
還有鎖失效的問題需要如何處理?

Redisson

可以使用Redisson來實(shí)現(xiàn)鎖,Redisson具有一個看門狗機(jī)制,Redisson實(shí)現(xiàn)鎖的方案是給鎖設(shè)置一個過期時間,當(dāng)業(yè)務(wù)還沒執(zhí)行完Redisson會更新一下鎖的失效時間,如果發(fā)生宕機(jī)情況鎖的有效時間過了自然就釋放了,而且Redisson是可重入鎖。下面看一下Redisson加鎖解鎖代碼

public class RedissonConfig {public static final String REDIS_ADDRESS_PORT = "redis://192.168.0.90:6380";    public static RedissonClient REDISSON_CLIENT;    public static synchronized RedissonClient getRedisson(){if(REDISSON_CLIENT == null) {
            Config config = new Config();            SingleServerConfig singleServerConfig = config.useSingleServer();            singleServerConfig.setAddress(REDIS_ADDRESS_PORT);            REDISSON_CLIENT = Redisson.create(config);        }return REDISSON_CLIENT;    }

}
@SneakyThrows@Overridepublic void run() {for (int i = 0; i < 10; i++) {
        RLock lock = RedissonConfig.getRedisson().getLock("COUNT_ADD_LOCK");        while(!lock.tryLock());        System.out.println("線程:" + Thread.currentThread().getName() + " 操作:" + COUNT++);        Thread.sleep(100);        lock.unlock();    }try {
        System.out.println("線程:" + Thread.currentThread().getName() + " 執(zhí)行完成");        semaphore.release();    } catch (Exception e) {
        e.printStackTrace();    }
}

多次的執(zhí)行結(jié)果都能達(dá)到預(yù)期值

Redis中怎樣實(shí)現(xiàn)分布式鎖

上述就是小編為大家分享的Redis中怎樣實(shí)現(xiàn)分布式鎖了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

本文標(biāo)題:Redis中怎樣實(shí)現(xiàn)分布式鎖
文章起源:http://vcdvsql.cn/article18/gjiedp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供關(guān)鍵詞優(yōu)化定制開發(fā)域名注冊App開發(fā)網(wǎng)站策劃外貿(mào)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

小程序開發(fā)