一、加鎖原因
二、原子操作
三、分佈式鎖
四、分佈式鎖常見問題
一、加鎖原因
在一些比較高併發的業務場景,經常聽到通過加鎖的方法實現線程安全。
下面簡單介紹一下
1.1 加鎖方式
數據庫鎖
數據庫本身提供了鎖機制,比如樂觀鎖、悲觀鎖等等。下面給出我之前寫的一篇博客,介紹一下mysql數據庫的鎖機制
Mysql的鎖機制
單體環境
Java線程層面,Java的jdk本身就提供了,比如synchronized和ReentrantLock可重入鎖。這是實現單體環境鎖的一種方法,這裏簡單介紹一下,並不對synchronized和ReentrantLock進行詳細介紹。
分佈式環境
上面介紹的都是單體環境的和數據庫層面的,下面介紹一下分佈式環境的解決方法。分佈式環境有兩種比較常用的解決方法,一種是通過Zookeeper實現分佈式;一種是通過Redis實現分佈式鎖。本博客比較詳細地介紹一下redis分佈式鎖,對於Zookeeper分佈式鎖有時間再寫博客介紹。
1.2 業務場景
爲什麼加鎖?從業務來說其實就是有業務場景,技術層面是爲了保證線程安全性,也就是說保證線程操作是原子操作。
下面簡單介紹一下一些業務場景
比如電商的秒殺場景,這就是一個高併發場景了,假如一個用戶購買了庫存只有10的8件商品,另外一個用戶也要購買5件商品,這兩個用戶是同時進行的,第一個用戶買了8件,庫存就只有2件了,第二個用戶再買5件,注意是和第一個用戶操作同時進行的,這時也是10-5,庫存只有5件了。假如第一個用戶搶佔了,庫存優先減8了,第一個用戶進行操作,同時進行,獲取到的庫存是10,這時就會出現業務問題了。
二、原子操作
原子操作定義
博客介紹一下原子操作,爲什麼說到原子操作呢?貌似和分佈式鎖不搭邊,其實不是的,我們說加鎖,其本質目的就是爲了實現線程操作是原子性的,也就是原子操作。
原子操作:是指不會被線程調度機制打斷的操作,而且期間不會有任何上下文切換(context switch)。
2.1 context switch
上面介紹一下上下文切換(context switch),上下文切換是計算機的cpu從一個任務,或者說進程,從一個任務(進程)切換到另外一個任務(進程),期間確保任務(進程)不衝突的過程。
在國外的whatis.techtarget網站有進行了比較詳細的定義
上下文切換定義
三、分佈式鎖
3.1 實現方式
可以實現方式 setnx+expire
在Redis中實現分佈式鎖,可以通過setnx和expire實現,setnx命令意思是set key if not exist,就是說已經有一個線程佔用了,就不執行set key操作
語法:setnx key value;expire是設置key的時間
這裏setnx key爲tkey,value爲tvalue
>setnx tkey tvalue
OK
>get tkey
tvalue
>expire tkey 5
OK
>del tkey
(integer) 1
先setnx,然後再給key加一個時間5秒,5秒後自動釋放鎖。當然一個進程執行過程還沒5秒也可以就直接刪除key。那麼假如在setnx過程出現異常,鎖就不能釋放。
爲了避免上面所說的分佈式鎖不能釋放問題,開源社區有很多分佈式解決方案,很多第三方庫,直到redis2.8版本,作者給出了一個很好的解決方案。
redis2.8版本對setnx命令和expire命令進行了拓展,使這兩個命令可以同時執行,也可以理解爲同個事務了。
語法:
setnx key ex time nx
例子,設置tkey,時間爲5秒
setnx tkey ex 5 nx
四、分佈式鎖常見問題
4.1 超時問題
假如在釋放鎖和另一個線程重新佔用鎖之間,執行時間過長,超過了鎖的超時設置,這時候就會出現,第一個線程的鎖已經被標記爲過期了,可是在臨界區的執行程序還沒執行,也就是說鎖並沒有真正釋放。這時候如果第二個線程持有了鎖,就會出現臨界區的代碼不能正常串行執行,因爲第一個線程的鎖在臨界區還沒真正釋放。
這是一種比較常見的Redis鎖不能釋放的超時問題。
通過網上資料,有提供了一種解決方案思路,是通過在set key的時候給value值加一個時間戳字符串或者一個特定的隨機數,比如uuid,可以表示特定線程的標識,這個標識要唯一。
然後在刪除key,重新加鎖的時候,校驗這個value是否爲第一個線程的,匹配正確才刪除key,這是一種方案,當然並不是很好的解決方案,只能說是相對安全的,因爲在高併發情況下面,線程的調用機制還是可以支持另外的線程持有鎖的。
4.2 集羣環境
下面介紹一下集羣環境的鎖問題,業務場景,假如一個線程在主服務器(master)釋放鎖的時候,master突然冗機了,也是就是說鎖還沒被釋放。這時集羣環境檢測到master機器冗機,就切換從服務器(slave)作爲主服務器,這時候,另外一個線程進來了,佔有了同一個鎖,也就是出現了兩個線程同時佔有同一個鎖的情況了。通過keepalive和redis實現主從服務器自動failover的方式或許可以解決問題。因爲並沒有實踐過,所以不做詳細解釋。這篇博客
Redis自從自動failover或許可以參考。
ReadLock算法
集羣環境的鎖同步是一個難題。上面的僅僅是我的想法並沒有實踐過,最近找到一個算法可以解決,ReadLock算法。readlock-py庫已經有對改算法進行實踐。ReadLock算法簡單原理就是通過先檢測set是否成功,set成功之後才向所有節點發送指令,釋放鎖。本博客並不對ReadLock算法做詳細介紹,有機會再寫博客介紹。
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper<www.thd540.com/ /artifactId>
<scope>provided<www.dasheng178.com /scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
第四步:在SpringBoot的屬性文件application.properties中配置JSP的路由
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp
第五步:修改Maven的pom.xml文件打包方式改成war(默認打包Jar,打包Jar包的方式使用Idea啓動是沒什麼問題,如果單獨運行Jar包就找不到JSP文件,如果改成War包即可)
<packaging>war</packaging>
SpringBoot中使用Thymeleaf
SpringBoot官方是推薦使用thymeleaf作爲優選的視圖解析器,所以SpringBoot對Thymeleaf的支持非常好,這裏僅僅演示SpringBoot如何選用Thymeleaf作用默認視圖解析器。
第一步:導入Thymeleaf的依賴
<dependency>
<groupId>org.springframework.boot<www.quwanyule157.com /groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
第二步:創建存放Thymeleaf模板文件夾,在Resources目錄下創建templates目錄
這個文件夾的名字可不是我麼隨便命名的啊,是SpringBoot在自動裝配Thymeleaf視圖解析器的時候就已經預定義好了,我們看一下它的定義源碼。
@ConfigurationProperties(prefix www.furggw.com= "spring.thymeleaf")
public class ThymeleafProperties {
private static final www.dasheng178.com Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
public static final String DEFAULT_PREFIX = www.mcyllpt.com/"classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
}
SpringBoot中使用Freemark
第一步:導入Maven依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
第二步:創建存放Freemark模板文件夾,在Resources目錄下創建templates目錄
@ConfigurationProperties(prefix www.feifanyule.cn/= "spring.freemarker")
public class FreeMarkerProperties extends AbstractTemplateViewResolverProperties {
public static final String DEFAULT_TEMPLATE_LOADER_PATH = "classpath:/templates/";
public static final String DEFAULT_www.wanchuang178.cn PREFIX = "";
public static final String DEFAULT_SUFFIX = ".ftl";
}
我們可以看到SpringBoot在自動裝配Freemarker視圖解析器默認是將模板文件放在classpath:/templates/路徑內,我們同樣可以在SpringBoot的配置文件中自行配置。
小提示:我在寫Freemark視圖解析器的時候並沒有將第一個JSP內部資源解析器給刪除掉,所以他們是並存的,所以我們可以知道SpringBoot在裝配他們的時候給予設定了優先級順序。從下圖可以看到他們的優先級順序;Freemarker>Thymeleaf>InternalResourceViewResolver`
Redis之分佈式鎖
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章
面試題:2018最全Redis面試題整理
UMUTech
2019-02-23 13:29:59
Redis+Twemproxy安裝與使用
Hellvenus
2019-02-23 00:12:48
Redis的安裝及碰到的問題
東666
2019-02-23 00:10:45
爲什麼要用Redis
男人7分熟
2019-02-22 23:59:10
Redis問題彙總
wx5922e1cc2aa76
2019-02-22 23:50:03
Redis的部署使用文檔
瀟瀟慕宇
2019-02-22 23:48:36
Redis的KEYS命令引起RDS數據庫雪崩,RDS發生兩次宕機,造成幾百萬的資金損失
Java陳序猿
2019-02-22 23:43:38
Redis集羣的高可用測試(含Jedis客戶端的使用)
1362802538
2019-02-22 23:37:06
構建高性能數據庫緩存之redis主從複製
晨風微涼
2019-02-22 23:35:08
Redis熱點Key發現及常見解決方案!
JAVA少女
2019-02-22 23:32:06
SpringBoot 之Thymeleaf模板.
Java進行中
2019-02-22 23:34:12
FreeMarker模板文件的組成及基本語法詳解(一)
oecp123
2019-02-23 00:00:48
Spring Boot中Jackson ObjectMapper應用詳解
Jason_川川
2019-02-22 23:47:50
Spring Boot加密配置屬性--Spring Cloud Vault詳解
Jason_川川
2019-02-22 23:46:42
Spring Boot HTTPS配置與後臺調用
Jason_川川
2019-02-22 23:46:42