真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

基于Redis的SETNX操作怎么實(shí)現(xiàn)分布式鎖

本文小編為大家詳細(xì)介紹“基于redis的SETNX操作怎么實(shí)現(xiàn)分布式鎖”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“基于Redis的SETNX操作怎么實(shí)現(xiàn)分布式鎖”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學(xué)習(xí)新知識吧。

創(chuàng)新互聯(lián)服務(wù)項(xiàng)目包括淄川網(wǎng)站建設(shè)、淄川網(wǎng)站制作、淄川網(wǎng)頁制作以及淄川網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,淄川網(wǎng)站推廣取得了明顯的社會效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到淄川省份的部分城市,未來相信會繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!

import com.jd.jim.cli.Cluster;

import java.io.IOException;

import java.util.concurrent.TimeUnit;

/**

 *

 * 基于Redis的SETNX操作實(shí)現(xiàn)的分布式鎖

 *

 * @author lzc.java@icloud.com

 *

 */

public class RedisDistributedLock {

    private Cluster redis;

    // 鎖的名字

    private String lockKey;

    // 鎖的值

    private String lockVal = "";

    // 默認(rèn)鎖的有效時(shí)長(毫秒)

    private long lockExpires;

    private boolean locked;

    // 當(dāng)前jvm內(nèi)持有該鎖的線程(if have one)  

    private Thread exclusiveOwnerThread;

    /**

     *

     * @param redis

     * @param lockKey  lockKey

     * @param lockExpires lockKey過期時(shí)間,單位:毫秒

     * @throws IOException

     */

    public RedisDistributedLock(Cluster redis, String lockKey, long lockExpires){

        this.redis = redis;

        this.lockKey = lockKey;

        this.lockExpires = lockExpires;

    }

    /**

     * 阻塞式獲取鎖 ,不過有超時(shí)時(shí)間,超過了tryGetLockTime還未獲取到鎖將直接返回false

     * @param tryGetLockTime

     * @param tryGetLockUnit

     * @return

     * @throws InterruptedException

     */

    protected boolean lock(long tryGetLockTime, TimeUnit tryGetLockUnit){

        try {

            // 超時(shí)控制 的時(shí)間可以從本地獲取, 因?yàn)檫@個和鎖超時(shí)沒有關(guān)系, 只是一段時(shí)間區(qū)間的控制

            long start = System.currentTimeMillis();

            long timeout = tryGetLockUnit.toMillis(tryGetLockTime); 

            //int tryTimes=0;

            while (System.currentTimeMillis() - start < timeout) {

                //tryTimes++;

                //鎖超時(shí)時(shí)間

                long lockExpireTime = System.currentTimeMillis() + lockExpires + 1;

                String stringOfLockExpireTime = String.valueOf(lockExpireTime);

                if (setnx(lockKey, stringOfLockExpireTime)) { // 獲取到鎖

                    // 成功獲取到鎖, 設(shè)置相關(guān)標(biāo)識

                    locked = true;

                    exclusiveOwnerThread = Thread.currentThread();

                    //System.out.println("拿到鎖了,哈哈:"+tryTimes);

                    return true;

                }

                //說明未獲取到鎖,進(jìn)一步檢查鎖是否已經(jīng)超時(shí)

                String lockVal=redis.get(lockKey);

                //是存在lockVal=null的情況的,C1客戶端獲取鎖,并且處理完后,DEL掉鎖,在DEL鎖之前。

                // C2通過SETNX向lockKey設(shè)置時(shí)間戳T0 發(fā)現(xiàn)有客戶端已經(jīng)獲取鎖,進(jìn)入GET操作。

                // 這時(shí)候C1客戶端DEL掉鎖成功。

                // C2向lockKey發(fā)送GET命令,獲取返回值T1(null)。

                if(lockVal!=null&&Long.parseLong(lockVal)

                    //表明已經(jīng)超時(shí)了,原來的線程可能可能出現(xiàn)意外未能及時(shí)釋放鎖

                    String oldLockVal=redis.getSet(lockKey,stringOfLockExpireTime);

                    //為什么會有下面這個判斷呢?因?yàn)槎嗑€程情況下可能同時(shí)有多個線程在這一時(shí)刻發(fā)現(xiàn)鎖過期,那么就會同時(shí)執(zhí)行g(shù)etSet獲取鎖操作,

                    //通過下面的比較,可以找到第一個執(zhí)行g(shù)etSet操作的線程,讓其獲得鎖,其它的線程則重試

                    //oldLockVal也存在null的情況,大家可以想想為什么

                    if(lockVal.equals(oldLockVal)){

                        redis.expire(lockKey, lockExpires, TimeUnit.MILLISECONDS);

                        // 成功獲取到鎖, 設(shè)置相關(guān)標(biāo)識

                        locked = true;

                        exclusiveOwnerThread = Thread.currentThread();

                        return true;

                    }

                }

                Thread.sleep(5L);

            }

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        return false;

    }

    /**

     * 非阻塞,立即返回是否獲取到鎖

     * @return

     */

    public boolean tryLock() {

        if (setnx(lockKey, lockVal)) { // 獲取到鎖

            //  成功獲取到鎖, 設(shè)置相關(guān)標(biāo)識

            locked = true;

            //setExclusiveOwnerThread(Thread.currentThread());

            exclusiveOwnerThread = Thread.currentThread();

            return true;

        }

        return false;

    }

    private boolean setnx(String lockKey, Object val) {

        if (redis.setNX(lockKey, String.valueOf(val))) {

            redis.expire(lockKey, lockExpires, TimeUnit.MILLISECONDS);

            return true;

        }

        return false;

    }

    public boolean isLocked() {

        return locked;

    }

    /**

     * 釋放鎖

     */

    public void unlock() {

        // 檢查當(dāng)前線程是否持有鎖

        if (Thread.currentThread() != exclusiveOwnerThread) {

            // 表明鎖并非當(dāng)前線程所持有,不應(yīng)該由當(dāng)前線程來釋放鎖

            System.out.println("表明鎖并非當(dāng)前線程所持有,不應(yīng)該由當(dāng)前線程來釋放鎖exclusiveOwnerThread:" + exclusiveOwnerThread + ",Thread.currentThread():"+Thread.currentThread()+",lockKey" + lockKey);

            return;

        }

        //gaohongtianluck 忽略了一個地方。用del命令釋放鎖,如果線程A獲得鎖之后運(yùn)行太久,久到另已經(jīng)獲得的鎖失效了。

        // 這時(shí)線程B進(jìn)來,取締了A上的鎖,線程B運(yùn)行到一半的時(shí)候,這時(shí)線程A也運(yùn)行完了,殺一個回馬槍把原本以為獲取到的鎖給del,

        // 實(shí)際上是B獲得的鎖,那么就會導(dǎo)致其他線程進(jìn)來競爭,而B還以為自己獨(dú)占鎖

        //回復(fù)Ffadsfoadfjaodjfalkd:我也在思考這個問題,我覺得有一種寫法可以盡量避免。在鎖的時(shí)候,如果鎖住了,回傳超時(shí)時(shí)間,作為解鎖時(shí)候的憑證,解鎖時(shí)傳入鎖的鍵值和憑證。我思考的解鎖時(shí)候有兩種寫法:

        //1、解鎖前get一下鍵值的value,判斷是不是和自己的憑證一樣。但這樣存在一些問題:

        //1)get時(shí)返回null的可能,此時(shí)表示有別的線程拿到鎖并用完釋放

        //2)get返回非null,但是不等于自身憑證。由于有g(shù)etset那一步,當(dāng)兩個競爭線程都在這個過程中時(shí),存在持有鎖的線程憑證不等于value,而value是稍慢那一步線程設(shè)置的value。

        //

        //2、解鎖前用憑證判斷鎖是否已經(jīng)超時(shí),如果沒有超時(shí),直接刪除;如果超時(shí),等著鎖自動過期就好,免得誤刪別人的鎖。但這種寫法同樣存在問題,由于線程調(diào)度的不確定性,判斷到刪除之間可能過去很久,并不是絕對意義上的正確解鎖。

        //

        //關(guān)于解鎖我只想到這么多,希望有幫助,歡迎拍磚多交流。

        //綜上所述,lzc.java實(shí)現(xiàn)采用了非常簡單的方法,如上所述,即超時(shí)的情況下可能會出現(xiàn)誤釋放鎖的場景,所以使用的時(shí)候就需要合理設(shè)置超時(shí)時(shí)間了

        redis.del(lockKey);

        exclusiveOwnerThread = null;

    }

}

讀到這里,這篇“基于Redis的SETNX操作怎么實(shí)現(xiàn)分布式鎖”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識點(diǎn)還需要大家自己動手實(shí)踐使用過才能領(lǐng)會,如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


本文標(biāo)題:基于Redis的SETNX操作怎么實(shí)現(xiàn)分布式鎖
網(wǎng)站鏈接:http://weahome.cn/article/iegpds.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部