摘要:廢話不多說,首先分享一個業務場景搶購。下面就是分布式鎖的解決方法。首先要加入的依賴,該類只有兩個功能,加鎖和解鎖,解鎖比較簡單,就是刪除當前的鍵值對。這時繼續執行,由于所以該線程獲取到鎖。
廢話不多說,首先分享一個業務場景-搶購。一個典型的高并發問題,所需的最關鍵字段就是庫存,在高并發的情況下每次都去數據庫查詢顯然是不合適的,因此把庫存信息存入Redis中,利用redis的鎖機制來控制并發訪問,是一個不錯的解決方案。
首先是一段業務代碼:
@Transactional public void orderProductMockDiffUser(String productId){ //1.查庫存 int stockNum = stock.get(productId); if(stocknum == 0){ throw new SellException(ProductStatusEnum.STOCK_EMPTY); //這里拋出的異常要是運行時異常,否則無法進行數據回滾,這也是spring中比較基礎的 }else{ //2.下單 orders.put(KeyUtil.genUniqueKey(),productId);//生成隨機用戶id模擬高并發 sotckNum = stockNum-1; try{ Thread.sleep(100); } catch (InterruptedExcption e){ e.printStackTrace(); } stock.put(productId,stockNum); } }
這里有一種比較簡單的解決方案,就是synchronized關鍵字。
public synchronized void orderProductMockDiffUser(String productId)
這就是java自帶的一種鎖機制,簡單的對函數加鎖和釋放鎖。但問題是這個實在是太慢了,感興趣的可以可以寫個接口用apache ab壓測一下。
ab -n 500 -c 100 http://localhost:8080/xxxxxxx
下面就是redis分布式鎖的解決方法。首先要了解兩個redis指令
SETNX 和 GETSET,可以在redis中文網上找到詳細的介紹。
SETNX就是set if not exist的縮寫,如果不存在就返回保存value并返回1,如果存在就返回0。
GETSET其實就是兩個指令GET和SET,首先會GET到當前key的值并返回,然后在設置當前Key為要設置Value。
首先我們先新建一個RedisLock類:
@Slf4j @Component public class RedisService { @Autowired private StringRedisTemplate stringRedisTemplate; /*** * 加鎖 * @param key * @param value 當前時間+超時時間 * @return 鎖住返回true */ public boolean lock(String key,String value){ if(stringRedisTemplate.opsForValue().setIfAbsent(key,value)){//setNX 返回boolean return true; } //如果鎖超時 *** String currentValue = stringRedisTemplate.opsForValue().get(key); if(!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue)這個項目是springboot的項目。首先要加入redis的pom依賴,該類只有兩個功能,加鎖和解鎖,解鎖比較簡單,就是刪除當前key的鍵值對。我們主要來說一說加鎖這個功能。
首先,鎖的value值是當前時間加上過期時間的時間戳,Long類型。首先看到用setiFAbsent方法也就是對應的SETNX,在沒有線程獲得鎖的情況下可以直接拿到鎖,并返回true也就是加鎖,最后沒有獲得鎖的線程會返回false。 最重要的是中間對于鎖超時的處理,如果沒有這段代碼,當秒殺方法發生異常的時候,后續的線程都無法得到鎖,也就陷入了一個死鎖的情況。我們可以假設CurrentValue為A,并且在執行過程中拋出了異常,這時進入了兩個value為B的線程來爭奪這個鎖,也就是走到了注釋*的地方。currentValue==A,這時某一個線程執行到了getAndSet(key,value)函數(某一時刻一定只有一個線程執行這個方法,其他要等待)。這時oldvalue也就是之前的value等于A,在方法執行過后,oldvalue會被設置為當前的value也就是B。這時繼續執行,由于oldValue==currentValue所以該線程獲取到鎖。而另一個線程獲取的oldvalue是B,而currentValue是A,所以他就獲取不到鎖啦。多線程還是有些亂的,需要好好想一想。
接下來就是在業務代碼中加鎖啦:首要要@Autowired注入剛剛RedisLock類,不要忘記對這個類加一個@Component注解否則無法注入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,"換個姿勢再試試") } //1.查庫存 int stockNum = stock.get(productId); if(stocknum == 0){ throw new SellException(ProductStatusEnum.STOCK_EMPTY); //這里拋出的異常要是運行時異常,否則無法進行數據回滾,這也是spring中比較基礎的 }else{ //2.下單 orders.put(KeyUtil.genUniqueKey(),productId);//生成隨機用戶id模擬高并發 sotckNum = stockNum-1; try{ Thread.sleep(100); } catch (InterruptedExcption e){ e.printStackTrace(); } stock.put(productId,stockNum); } redisLock.unlock(productId,String.valueOf(time)); }大功告成了!比synchronized快了不知道多少倍,再也不會被老板罵了!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67659.html
摘要:在大流量程序開發中,必然會遇到高并發的應用的場景。樂觀鎖實現秒殺功能它的優點如下消息隊列對內存消耗較大,個請求,需要操作出隊列。需要結合實際的業務場景嵌入本文的核心實現邏輯。 在大流量程序開發中,必然會遇到高并發的應用的場景。解決方案大致分為兩個方向,消息隊列、鎖 redis 實現消息隊列核心簡單版本 $key = quque; /** ...
摘要:實現思路實現分布式鎖思路思路很簡單,主要用到的函數是,這個應該是實現分布式鎖最主要的函數。實現任務隊列這里的實現會用到上面的分布式的鎖機制,主要是用到了里的有序集合這一數據結構。 實現思路 1.Redis實現分布式鎖思路 思路很簡單,主要用到的redis函數是setnx(),這個應該是實現分布式鎖最主要的函數。首先是將某一任務標識名(這里用Lock:order作為標識名的例子)作...
閱讀 1772·2021-11-15 11:37
閱讀 3044·2021-11-04 16:05
閱讀 1910·2021-10-27 14:18
閱讀 2742·2021-08-12 13:30
閱讀 2486·2019-08-29 14:18
閱讀 2076·2019-08-29 13:07
閱讀 2004·2019-08-27 10:54
閱讀 2714·2019-08-26 12:15