亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Redis分布式鎖解決搶購(gòu)問(wèn)題

taoszu / 755人閱讀

摘要:廢話不多說(shuō),首先分享一個(gè)業(yè)務(wù)場(chǎng)景搶購(gòu)。下面就是分布式鎖的解決方法。首先要加入的依賴,該類只有兩個(gè)功能,加鎖和解鎖,解鎖比較簡(jiǎn)單,就是刪除當(dāng)前的鍵值對(duì)。這時(shí)繼續(xù)執(zhí)行,由于所以該線程獲取到鎖。

廢話不多說(shuō),首先分享一個(gè)業(yè)務(wù)場(chǎng)景-搶購(gòu)。一個(gè)典型的高并發(fā)問(wèn)題,所需的最關(guān)鍵字段就是庫(kù)存,在高并發(fā)的情況下每次都去數(shù)據(jù)庫(kù)查詢顯然是不合適的,因此把庫(kù)存信息存入Redis中,利用redis的鎖機(jī)制來(lái)控制并發(fā)訪問(wèn),是一個(gè)不錯(cuò)的解決方案。

首先是一段業(yè)務(wù)代碼:

@Transactional
public void orderProductMockDiffUser(String productId){
    //1.查庫(kù)存
    int stockNum  = stock.get(productId);
    if(stocknum == 0){
        throw new SellException(ProductStatusEnum.STOCK_EMPTY);
        //這里拋出的異常要是運(yùn)行時(shí)異常,否則無(wú)法進(jìn)行數(shù)據(jù)回滾,這也是spring中比較基礎(chǔ)的   
    }else{
        //2.下單
        orders.put(KeyUtil.genUniqueKey(),productId);//生成隨機(jī)用戶id模擬高并發(fā)
        sotckNum = stockNum-1;
        try{
            Thread.sleep(100);
        } catch (InterruptedExcption e){
            e.printStackTrace();
        }
        stock.put(productId,stockNum);
    }
}

這里有一種比較簡(jiǎn)單的解決方案,就是synchronized關(guān)鍵字。

public synchronized void orderProductMockDiffUser(String productId)

這就是java自帶的一種鎖機(jī)制,簡(jiǎn)單的對(duì)函數(shù)加鎖和釋放鎖。但問(wèn)題是這個(gè)實(shí)在是太慢了,感興趣的可以可以寫(xiě)個(gè)接口用apache ab壓測(cè)一下。

ab -n 500 -c 100 http://localhost:8080/xxxxxxx

下面就是redis分布式鎖的解決方法。首先要了解兩個(gè)redis指令
SETNX 和 GETSET,可以在redis中文網(wǎng)上找到詳細(xì)的介紹。
SETNX就是set if not exist的縮寫(xiě),如果不存在就返回保存value并返回1,如果存在就返回0。
GETSET其實(shí)就是兩個(gè)指令GET和SET,首先會(huì)GET到當(dāng)前key的值并返回,然后在設(shè)置當(dāng)前Key為要設(shè)置Value。

首先我們先新建一個(gè)RedisLock類:

@Slf4j
@Component
public class RedisService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    /***
     * 加鎖
     * @param key
     * @param value 當(dāng)前時(shí)間+超時(shí)時(shí)間
     * @return 鎖住返回true
     */
    public boolean lock(String key,String value){
        if(stringRedisTemplate.opsForValue().setIfAbsent(key,value)){//setNX 返回boolean
            return true;
        }
        //如果鎖超時(shí) ***
        String currentValue = stringRedisTemplate.opsForValue().get(key);
        if(!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue)

這個(gè)項(xiàng)目是springboot的項(xiàng)目。首先要加入redis的pom依賴,該類只有兩個(gè)功能,加鎖和解鎖,解鎖比較簡(jiǎn)單,就是刪除當(dāng)前key的鍵值對(duì)。我們主要來(lái)說(shuō)一說(shuō)加鎖這個(gè)功能。
首先,鎖的value值是當(dāng)前時(shí)間加上過(guò)期時(shí)間的時(shí)間戳,Long類型。首先看到用setiFAbsent方法也就是對(duì)應(yīng)的SETNX,在沒(méi)有線程獲得鎖的情況下可以直接拿到鎖,并返回true也就是加鎖,最后沒(méi)有獲得鎖的線程會(huì)返回false。 最重要的是中間對(duì)于鎖超時(shí)的處理,如果沒(méi)有這段代碼,當(dāng)秒殺方法發(fā)生異常的時(shí)候,后續(xù)的線程都無(wú)法得到鎖,也就陷入了一個(gè)死鎖的情況。我們可以假設(shè)CurrentValue為A,并且在執(zhí)行過(guò)程中拋出了異常,這時(shí)進(jìn)入了兩個(gè)value為B的線程來(lái)爭(zhēng)奪這個(gè)鎖,也就是走到了注釋*的地方。currentValue==A,這時(shí)某一個(gè)線程執(zhí)行到了getAndSet(key,value)函數(shù)(某一時(shí)刻一定只有一個(gè)線程執(zhí)行這個(gè)方法,其他要等待)。這時(shí)oldvalue也就是之前的value等于A,在方法執(zhí)行過(guò)后,oldvalue會(huì)被設(shè)置為當(dāng)前的value也就是B。這時(shí)繼續(xù)執(zhí)行,由于oldValue==currentValue所以該線程獲取到鎖。而另一個(gè)線程獲取的oldvalue是B,而currentValue是A,所以他就獲取不到鎖啦。多線程還是有些亂的,需要好好想一想。
接下來(lái)就是在業(yè)務(wù)代碼中加鎖啦:首要要@Autowired注入剛剛RedisLock類,不要忘記對(duì)這個(gè)類加一個(gè)@Component注解否則無(wú)法注入

private static final int TIMEOUT= 10*1000;
@Transactional
public void orderProductMockDiffUser(String productId){
     long time = System.currentTimeMillions()+TIMEOUT;
   if(!redislock.lock(productId,String.valueOf(time)){
    throw new SellException(101,"換個(gè)姿勢(shì)再試試")
    }
    //1.查庫(kù)存
    int stockNum  = stock.get(productId);
    if(stocknum == 0){
        throw new SellException(ProductStatusEnum.STOCK_EMPTY);
        //這里拋出的異常要是運(yùn)行時(shí)異常,否則無(wú)法進(jìn)行數(shù)據(jù)回滾,這也是spring中比較基礎(chǔ)的   
    }else{
        //2.下單
        orders.put(KeyUtil.genUniqueKey(),productId);//生成隨機(jī)用戶id模擬高并發(fā)
        sotckNum = stockNum-1;
        try{
            Thread.sleep(100);
        } catch (InterruptedExcption e){
            e.printStackTrace();
        }
        stock.put(productId,stockNum);
    }
    redisLock.unlock(productId,String.valueOf(time));
}

大功告成了!比synchronized快了不知道多少倍,再也不會(huì)被老板罵了!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/67659.html

相關(guān)文章

  • Redis--秒殺場(chǎng)景應(yīng)用

    摘要:在大流量程序開(kāi)發(fā)中,必然會(huì)遇到高并發(fā)的應(yīng)用的場(chǎng)景。樂(lè)觀鎖實(shí)現(xiàn)秒殺功能它的優(yōu)點(diǎn)如下消息隊(duì)列對(duì)內(nèi)存消耗較大,個(gè)請(qǐng)求,需要操作出隊(duì)列。需要結(jié)合實(shí)際的業(yè)務(wù)場(chǎng)景嵌入本文的核心實(shí)現(xiàn)邏輯。 在大流量程序開(kāi)發(fā)中,必然會(huì)遇到高并發(fā)的應(yīng)用的場(chǎng)景。解決方案大致分為兩個(gè)方向,消息隊(duì)列、鎖 redis 實(shí)現(xiàn)消息隊(duì)列核心簡(jiǎn)單版本 $key = quque; /** ...

    lovXin 評(píng)論0 收藏0
  • php+redis實(shí)現(xiàn)搶購(gòu)功能

    摘要:實(shí)現(xiàn)思路實(shí)現(xiàn)分布式鎖思路思路很簡(jiǎn)單,主要用到的函數(shù)是,這個(gè)應(yīng)該是實(shí)現(xiàn)分布式鎖最主要的函數(shù)。實(shí)現(xiàn)任務(wù)隊(duì)列這里的實(shí)現(xiàn)會(huì)用到上面的分布式的鎖機(jī)制,主要是用到了里的有序集合這一數(shù)據(jù)結(jié)構(gòu)。 實(shí)現(xiàn)思路 1.Redis實(shí)現(xiàn)分布式鎖思路   思路很簡(jiǎn)單,主要用到的redis函數(shù)是setnx(),這個(gè)應(yīng)該是實(shí)現(xiàn)分布式鎖最主要的函數(shù)。首先是將某一任務(wù)標(biāo)識(shí)名(這里用Lock:order作為標(biāo)識(shí)名的例子)作...

    BetaRabbit 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<