2020最新的Spring Boot 分散式鎖的具體實現(內附代碼)

来源:https://www.cnblogs.com/MonsterJ/archive/2020/07/07/13262758.html
-Advertisement-
Play Games

前言 面試總是會被問到有沒有用過分散式鎖、redis 鎖,大部分讀者平時很少接觸到,所以只能很無奈的回答 “沒有”。本文通過 Spring Boot 整合 redisson 來實現分散式鎖,並結合 demo 測試結果。 首先看下大佬總結的圖 正文 添加依賴 <!--redis--> <depende ...


前言

面試總是會被問到有沒有用過分散式鎖、redis 鎖,大部分讀者平時很少接觸到,所以只能很無奈的回答 “沒有”。本文通過 Spring Boot 整合 redisson 來實現分散式鎖,並結合 demo 測試結果。

首先看下大佬總結的圖

正文

添加依賴

<!--redis-->
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-data-redis
</artifactId>
</dependency>
<!--redisson-->
<dependency>
<groupId>
org.redisson
</groupId>
<artifactId>
redisson-spring-boot-starter
</artifactId>
<version>
3.10.6
</version>
</dependency>

配置信息

spring:
# redis
  redis:
    host: 
47.103
.
5.190
    port: 
6379
    jedis:
      pool:
# 連接池最大連接數(使用負值表示沒有限制)
        max-active: 100
# 連接池中的最小空閑連接
        max-idle: 10
# 連接池最大阻塞等待時間(使用負值表示沒有限制)
        max-wait: -1
# 連接超時時間(毫秒)
      timeout: 5000
#預設是索引為0的資料庫
      database: 0

配置類

/**
 * redisson 配置,下麵是單節點配置:
 *
 * @author gourd
 */
@Configuration
publicclass
RedissonConfig
{
@Value
(
"${spring.redis.host}"
)
private
String
 host;
@Value
(
"${spring.redis.port}"
)
private
String
 port;
@Value
(
"${spring.redis.password:}"
)
private
String
 password;

@Bean
public
RedissonClient
 redissonClient() {
Config
 config = 
new
Config
();
//單節點
        config.useSingleServer().setAddress(
"redis://"
+ host + 
":"
+ port);
if
(
StringUtils
.isEmpty(password)) {
            config.useSingleServer().setPassword(
null
);
} 
else
{
            config.useSingleServer().setPassword(password);
}
//添加主從配置
// config.useMasterSlaveServers().setMasterAddress("").setPassword("").addSlaveAddress(new String[]{"",""});
// 集群模式配置 setScanInterval()掃描間隔時間,單位是毫秒, //可以用"rediss://"來啟用SSL連接
// config.useClusterServers().setScanInterval(2000).addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001").addNodeAddress("redis://127.0.0.1:7002");
return
Redisson
.create(config);
}
}      

Redisson 工具類

/**
 * redis分散式鎖幫助類
 *
 * @author gourd
 *
 */
publicclass
RedisLockUtil
{

privatestatic
DistributedLocker
 distributedLocker = 
SpringContextHolder
.getBean(
"distributedLocker"
,
DistributedLocker
.
class
);

/**
     * 加鎖
     * @param lockKey
     * @return
     */
publicstatic
RLock
lock
(
String
 lockKey) {
return
 distributedLocker.
lock
(lockKey);
}

/**
     * 釋放鎖
     * @param lockKey
     */
publicstaticvoid
 unlock(
String
 lockKey) {
        distributedLocker.unlock(lockKey);
}

/**
     * 釋放鎖
     * @param lock
     */
publicstaticvoid
 unlock(
RLock
lock
) {
        distributedLocker.unlock(
lock
);
}

/**
     * 帶超時的鎖
     * @param lockKey
     * @param timeout 超時時間   單位:秒
     */
publicstatic
RLock
lock
(
String
 lockKey, 
int
 timeout) {
return
 distributedLocker.
lock
(lockKey, timeout);
}

/**
     * 帶超時的鎖
     * @param lockKey
     * @param unit 時間單位
     * @param timeout 超時時間
     */
publicstatic
RLock
lock
(
String
 lockKey, 
int
 timeout,
TimeUnit
 unit ) {
return
 distributedLocker.
lock
(lockKey, unit, timeout);
}

/**
     * 嘗試獲取鎖
     * @param lockKey
     * @param waitTime 最多等待時間
     * @param leaseTime 上鎖後自動釋放鎖時間
     * @return
     */
publicstaticboolean
 tryLock(
String
 lockKey, 
int
 waitTime, 
int
 leaseTime) {
return
 distributedLocker.tryLock(lockKey, 
TimeUnit
.SECONDS, waitTime, leaseTime);
}

/**
     * 嘗試獲取鎖
     * @param lockKey
     * @param unit 時間單位
     * @param waitTime 最多等待時間
     * @param leaseTime 上鎖後自動釋放鎖時間
     * @return
     */
publicstaticboolean
 tryLock(
String
 lockKey, 
TimeUnit
 unit, 
int
 waitTime, 
int
 leaseTime) {
return
 distributedLocker.tryLock(lockKey, unit, waitTime, leaseTime);
}

/**
     * 獲取計數器
     *
     * @param name
     * @return
     */
publicstatic
RCountDownLatch
 getCountDownLatch(
String
 name){
return
 distributedLocker.getCountDownLatch(name);
}

/**
     * 獲取信號量
     *
     * @param name
     * @return
     */
publicstatic
RSemaphore
 getSemaphore(
String
 name){
return
 distributedLocker.getSemaphore(name);
}
}

底層封裝

/**
 * @author gourd
 */
publicinterface
DistributedLocker
{

RLock
lock
(
String
 lockKey);

RLock
lock
(
String
 lockKey, 
int
 timeout);

RLock
lock
(
String
 lockKey, 
TimeUnit
 unit, 
int
 timeout);

boolean
 tryLock(
String
 lockKey, 
TimeUnit
 unit, 
int
 waitTime, 
int
 leaseTime);

void
 unlock(
String
 lockKey);

void
 unlock(
RLock
lock
);
}

/**
 * @author gourd
 */
@Component
publicclass
RedisDistributedLocker
implements
DistributedLocker
{

@Autowired
private
RedissonClient
 redissonClient;

@Override
public
RLock
lock
(
String
 lockKey) {
RLock
lock
= redissonClient.getLock(lockKey);
lock
.
lock
();
returnlock
;
}

@Override
public
RLock
lock
(
String
 lockKey, 
int
 leaseTime) {
RLock
lock
= redissonClient.getLock(lockKey);
lock
.
lock
(leaseTime, 
TimeUnit
.SECONDS);
returnlock
;
}

@Override
public
RLock
lock
(
String
 lockKey, 
TimeUnit
 unit ,
int
 timeout) {
RLock
lock
= redissonClient.getLock(lockKey);
lock
.
lock
(timeout, unit);
returnlock
;
}

@Override
publicboolean
 tryLock(
String
 lockKey, 
TimeUnit
 unit, 
int
 waitTime, 
int
 leaseTime) {
RLock
lock
= redissonClient.getLock(lockKey);
try
{
returnlock
.tryLock(waitTime, leaseTime, unit);
} 
catch
(
InterruptedException
 e) {
returnfalse
;
}
}

@Override
publicvoid
 unlock(
String
 lockKey) {
RLock
lock
= redissonClient.getLock(lockKey);
lock
.unlock();
}

@Override
publicvoid
 unlock(
RLock
lock
) {
lock
.unlock();
}
}

測試

模擬併發測試

/**
 * redis分散式鎖控制器
 * @author gourd
 * @since 2019-07-30
 */
@RestController
@Api
(tags = 
"redisson"
, description = 
"redis分散式鎖控制器"
)
@RequestMapping
(
"/redisson"
)
@Slf4j
publicclass
RedissonLockController
{

/**
     * 鎖測試共用變數
     */
private
Integer
 lockCount = 
10
;

/**
     * 無鎖測試共用變數
     */
private
Integer
 count = 
10
;

/**
     * 模擬線程數
     */
privatestaticint
 threadNum = 
10
;

/**
     * 模擬併發測試加鎖和不加鎖
     * @return
     */
@GetMapping
(
"/test"
)
@ApiOperation
(value = 
"模擬併發測試加鎖和不加鎖"
)
publicvoidlock
(){
// 計數器
final
CountDownLatch
 countDownLatch = 
new
CountDownLatch
(
1
);
for
(
int
 i = 
0
; i < threadNum; i ++) {
MyRunnable
 myRunnable = 
new
MyRunnable
(countDownLatch);
Thread
 myThread = 
new
Thread
(myRunnable);
            myThread.start();
}
// 釋放所有線程
        countDownLatch.countDown();
}

/**
     * 加鎖測試
     */
privatevoid
 testLockCount() {
String
 lockKey = 
"lock-test"
;
try
{
// 加鎖,設置超時時間2s
RedisLockUtil
.
lock
(lockKey,
2
, 
TimeUnit
.SECONDS);
            lockCount--;
            log.info(
"lockCount值:"
+lockCount);
}
catch
(
Exception
 e){
            log.error(e.getMessage(),e);
}
finally
{
// 釋放鎖
RedisLockUtil
.unlock(lockKey);
}
}

/**
     * 無鎖測試
     */
privatevoid
 testCount() {
        count--;
        log.info(
"count值:"
+count);
}


publicclass
MyRunnable
implements
Runnable
{
/**
         * 計數器
         */
final
CountDownLatch
 countDownLatch;

public
MyRunnable
(
CountDownLatch
 countDownLatch) {
this
.countDownLatch = countDownLatch;
}

@Override
publicvoid
 run() {
try
{
// 阻塞當前線程,直到計時器的值為0
                countDownLatch.
await
();
} 
catch
(
InterruptedException
 e) {
                log.error(e.getMessage(),e);
}
// 無鎖操作
            testCount();
// 加鎖操作
            testLockCount();
}

}

}

調用介面後列印值:

測試結果

根據列印結果可以明顯看到,未加鎖的 count-- 後值是亂序的,而加鎖後的結果和我們預期的一樣。

由於條件問題沒辦法測試分散式的併發。只能模擬單服務的這種併發,但是原理是一樣,希望對大家有幫助。如有錯誤之處,歡迎指正。

最後

私信回覆 資料 領取一線大廠Java面試題總結+各知識點學習思維導+一份300頁pdf文檔的Java核心知識點總結!

這些資料的內容都是面試時面試官必問的知識點,篇章包括了很多知識點,其中包括了有基礎知識、Java集合、JVM、多線程併發、spring原理、微服務、Netty 與RPC 、Kafka、日記、設計模式、Java演算法、資料庫、Zookeeper、分散式緩存、數據結構等等。

file


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.file類的方法 1.getName():獲取文件名 2.getPath():獲取文件路徑,設定是相對路徑得到的就是相對路徑,絕對路徑同里 3.getAbsolutePath():獲取文件絕對路徑 4.getParent():獲取文件父類路徑 5.exists():判斷文件是否存在 6.isFi ...
  • 其實spring boot攔截器的配置方式和springMVC差不多,只有一些小的改變需要註意下就ok了。下麵主要介紹兩種常用的攔截器: 一、基於URL實現的攔截器: public class LoginInterceptor extends HandlerInterceptor{ /** * 在請 ...
  • 點擊藍色“程式員書單”關註我喲 加個“星標”,每天帶你看好文,讀好書! 本文轉自知乎:https://zhuanlan.zhihu.com/p/34994820 最近在看程式員的自我修養 (豆瓣),作者陳逸鶴,一本寫給程式員的思考書,其實市面上類似題材的書也不少,比如我看過的另外兩本內外兼修:程式員 ...
  • 關註公眾號【程式員書單】後回覆“book”即可領取30+精品免費電子書 ​ Go 是一種簡單、小巧、令人愉悅的語言。它也有一些犄角旮旯,但絕大部分是經過精心設計的。它的學習速度令人難以置信,並且規避了其他語言中一些不那麼廣為人知的特性。 現如今越來越多的互聯網公司開始使用go語言,有的初創公司開始使 ...
  • 前言 本文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯繫我們以作處理。 作者:打磨蝦 “遲到”了一個月的高考終於要來了。 正好我得到了一份山東新高考模擬考的成績和山東考試院公佈的一分一段表,以及過去三年的普通高考本科普通批首次志願錄取情況統計。2 ...
  • 一. 安裝依賴包 yum install -y wget yum install -y gcc-c++ yum install -y zlib-devel perl-ExtUtils-MakeMaker yum -y install curl-devel expat-devel gettext-de ...
  • 解決辦法: https://tunatore.wordpress.com/2015/06/15/org-jboss-tools-vpe-xulrunner-xulrunnerbundlenotfoundexception-bundle-org-mozilla-xulrunner-win32-win3 ...
  • **前言** Topological sort 又稱 Topological order,這個名字有點迷惑性,因為拓撲排序並不是一個純粹的排序演算法,它只是針對某一類圖,找到一個可以執行的線性順序。 這個演算法聽起來高大上,如今的面試也很愛考,比如當時我在面我司時有整整一輪是基於拓撲排序的設計。 但它其 ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...