1. 程式人生 > >2020最新的Spring Boot 分散式鎖的具體實現(內附程式碼)

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

# 前言 面試總是會被問到有沒有用過分散式鎖、redis 鎖,大部分讀者平時很少接觸到,所以只能很無奈的回答 “沒有”。本文通過 Spring Boot 整合 redisson 來實現分散式鎖,並結合 demo 測試結果。 **首先看下大佬總結的圖** ![](https://img2020.cnblogs.com/other/1973721/202007/1973721-20200707193957444-222441247.png) # 正文 **新增依賴** ```java ``` 配置資訊 ```java 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 ``` # 配置類 ```java /** * 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 工具類 ```java /** * 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); } } ``` # 底層封裝 ```java /** * @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(); } } ``` # 測試 **模擬併發測試** ```java /** * 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(); } } } ``` # 呼叫介面後列印值: ![](https://img2020.cnblogs.com/other/1973721/202007/1973721-20200707194005139-1524594017.png) # 測試結果 根據列印結果可以明顯看到,未加鎖的 count-- 後值是亂序的,而加鎖後的結果和我們預期的一樣。 由於條件問題沒辦法測試分散式的併發。只能模擬單服務的這種併發,但是原理是一樣,希望對大家有幫助。如有錯誤之處,歡迎指正。 # 最後 >私信回覆 資料 領取一線大廠Java面試題總結+各知識點學習思維導+一份300頁pdf文件的Java核心知識點總結! 這些資料的內容都是面試時面試官必問的知識點,篇章包括了很多知識點,其中包括了有基礎知識、Java集合、JVM、多執行緒併發、spring原理、微服務、Netty 與RPC 、Kafka、日記、設計模式、Java演算法、資料庫、Zookeeper、分散式快取、資料結構等等。 ![file](https://img2020.cnblogs.com/other/1973721/202007/1973721-20200707194010540-1152282690.gif)