當我併發測試時:
這tm肯定不行啊,這就超賣了,明明沒這麼多商品,結果還賣出去了。。。
首先,synchronized的確是乙個解決辦法,而且也很簡單,在方法前面加乙個synchronized關鍵字。
但是通過壓測,發現請求變的很慢,因為:
synchronized就用乙個鎖把這個方法鎖住了,每次訪問這個方法,只會有乙個執行緒,所以這就是它導致慢的原因。通過這種方式,保證這個方法中的**都是單執行緒來處理,不會出什麼問題。
同時,使用synchronized還是存在一些問題的,首先,它無法做到細粒度的控制,比如同一時間有秒殺a商品和b商品的請求,都進入到了這個方法,雖然秒殺a商品的人很多,但是秒殺b商品的人很少,但是即使是買b商品,進入到了這個方法,也會一樣的慢。
最重要的是,它只適合單點的情況。如果以後程式水平擴充套件了,弄了個集群,很顯然,負載均衡之後,不同的使用者看到的結果一定是五花八門的。
所以,還是使用更好的辦法,使用redis分布式鎖。
1、兩個redis的命令
setnx key value 簡單來說,setnx就是,如果沒有這個key,那麼就set乙個key-value, 但是如果這個key已經存在,那麼將不會再次設定,get出來的value還是最開始set進去的那個value.
**中還專門講到可以使用!setnx加鎖,如果獲得鎖,返回1,如果返回0,那麼該鍵已經被其他的客戶端鎖定。
並且也提到了如何處理死鎖。
getset key value 這個就更簡單了,先通過key獲取value,然後再將新的value set進去。
2、redis分布式鎖的實現
我們希望的,無非就是這一段**,能夠單執行緒的去訪問,因此在這段**之前給他加鎖,相應的,這段**後面要給它解鎖:
2.1 引入redis依賴
org.springframework.boot2.2 配置redisspring-boot-starter-data-redis
spring:2.3 編寫加鎖和解鎖的方法redis:
host: localhost
port: 6379
package com.vito.service;為什麼要有避免死鎖的一步呢?import org.slf4j.logger;
import org.slf4j.logge***ctory;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.data.redis.core.stringredistemplate;
import org.springframework.stereotype.component;
import org.springframework.util.stringutils;
/** * created by vitoyi on 2018/4/5.
*/@component
public class redislock
//避免死鎖,且只讓乙個執行緒拿到鎖
string currentvalue = redistemplate.opsforvalue().get(key);
//如果鎖過期了
if (!stringutils.isempty(currentvalue) && long.parselong(currentvalue) < system.currenttimemillis())
} return false;
} /**
* 解鎖
* @param key
* @param value
*/ public void unlock(string key, string value)
} catch (exception e) ", e);
} }}
假設沒有『避免死鎖』這一步,結果在執行到下單**的時候出了問題,畢竟運算元據庫、網路、io的時候拋了個異常,這個異常是偶然丟擲來的,就那麼偶爾一次,那麼會導致解鎖步驟不去執行,這時候就沒有解鎖,後面的請求進來自然也或得不到鎖,這就被稱之為死鎖。
而這裡的『避免死鎖』,就是給鎖加了乙個過期時間,如果鎖超時了,就返回true,解開之前的那個死鎖。
2.4 下單**中引入加鎖和解鎖,確保只有乙個執行緒操作
@autowired這樣再來跑幾次壓測,就不會超賣了:private redislock redislock;
@override
@transactional
public string seckill(integer id)throws runtimeexception
//查庫存
if(product.getstock()==0) throw new runtimeexception("已經賣光");
//寫入訂單表
order order=new order();
order.setproductid(product.getid());
order.setproductname(product.getname());
//減庫存
product.setprice(null);
product.setname(null);
product.setstock(product.getstock()-1);
//解鎖
redislock.unlock(string.valueof(id),string.valueof(time));
return findproductinfo(id);
}
封裝-----
* @title: checksoldcountbyredisdate
* @description: 搶購的計數處理(用於處理超賣)
* @param @param key 購買計數的key
* @param @param limitcount 總的限購數量
* @param @param buycount 當前購買數量
* @param @param enddate 搶購結束時間
* @param @param lock 鎖的名稱與undielock方法的lock相同
* @param @param expire 鎖占有的時長(毫秒)
* @param @return 設定檔案
* @return boolean 返回型別
* @throws
private boolean checksoldcountbyredisdate(string key, int limitcount, int buycount, date enddate, string lock, int expire) else else else catch (exception e) {
e.printstacktrace();
return locked;
* @title: getdateaddmillsecond
* @description: (todo)取將來時間
* @param @param date
* @param @param millsecond
* @param @return 設定檔案
* @return date 返回型別
* @throws
public static date getdateadd(date date, int expire, int idate) {
calendar calendar = calendar.getinstance();
if (null != date) {// 預設當前時間
calendar.settime(date);
calendar.add(idate, expire);
return calendar.gettime();
* 刪除對應的value
* @param key
public static void remove(final string key) {
if (exists(key)) {
redistemplate.delete(key);
* 判斷快取中是否有對應的value
* @param key
* @return
public static boolean exists(final string key) {
return stringredistemplate.haskey(key);
private static stringredistemplate stringredistemplate = ((stringredistemplate) springcontextholder.getbean("stringredistemplate"));
分布式鎖實現之redis
利用redis設計分布式鎖主要用下面這三個命令 setnx setnx key val 當且僅當key不存在時,set乙個key為val的字串,返回1 若key存在,則什麼都不做,返回0。expire expire key timeout 為key設定乙個超時時間,單位為second,超過這個時間鎖...
分布式鎖 使用Redis實現分布式鎖
關於分布式鎖的實現,我的前一篇文章講解了如何使用zookeeper實現分布式鎖。關於分布式鎖的背景此處不再做贅述,我們直接討論下如何使用redis實現分布式鎖。關於redis,筆主不打算做長篇大論的介紹,只介紹下redis優秀的特性。支援豐富的資料型別,如string list map set zs...
redis實現分布式鎖
隨便 系統越來越大,各功能模組除了垂直切割以外,同時也得做集群處理,那麼問題來了,在多執行緒情況下對於資源的競爭就需要乙個統一的訪問限制。以選課系統為例子,集群中各節點對課程可選數量同時操作,這裡就需要同步了,否則會導致最後選到的數量比可選的數量大,這裡我們的分布式鎖就派上用場了。利用redis來實...