springboot 集成redission 以及分布式鎖的使用詳解
更新時間:2021年10月19日 09:59:19 作者:介寒食
這篇文章主要介紹了springboot 集成redission 以及分布式鎖的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
springboot集成redission及分布式鎖的使用
1、引入jar包
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.4</version>
</dependency>
2、增加Configuration類
@Configuration
public class 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 getRedisson() {
Config config = new Config();
config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password);
return Redisson.create(config);
}
}
3、使用redission分布式鎖
@Autowired private RedissonClient redissonClient;
//方法區(qū)
String key = "aa:bb:cc:01";
RLock rLock =redissonClient.getLock(key);
try{<br>// 嘗試加鎖,最多等待1秒,上鎖以后10秒自動解鎖<br>// 沒有Watch Dog ,10s后自動釋放
boolean res = rLock.tryLock(1,10, TimeUnit.SECONDS);
if(!res){
return new GeneralVO<>(400, "請勿重復(fù)提交", false);
}
}finally{
rLock.unlock();
}
private void redissonDoc() throws InterruptedException {
//1. 普通的可重入鎖
RLock lock = redissonClient.getLock("generalLock");
// 拿鎖失敗時會不停的重試
// 具有Watch Dog 自動延期機制 默認(rèn)續(xù)30s 每隔30/3=10 秒續(xù)到30s
lock.lock();
// 嘗試拿鎖10s后停止重試,返回false
// 具有Watch Dog 自動延期機制 默認(rèn)續(xù)30s
boolean res1 = lock.tryLock(10, TimeUnit.SECONDS);
// 拿鎖失敗時會不停的重試
// 沒有Watch Dog ,10s后自動釋放
lock.lock(10, TimeUnit.SECONDS);
// 嘗試拿鎖100s后停止重試,返回false
// 沒有Watch Dog ,10s后自動釋放
boolean res2 = lock.tryLock(100, 10, TimeUnit.SECONDS);
//2. 公平鎖 保證 Redisson 客戶端線程將以其請求的順序獲得鎖
RLock fairLock = redissonClient.getFairLock("fairLock");
//3. 讀寫鎖 沒錯與JDK中ReentrantLock的讀寫鎖效果一樣
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("readWriteLock");
readWriteLock.readLock().lock();
readWriteLock.writeLock().lock();
}
Springboot整合Redisson 鎖
Redisson是一個在Redis的基礎(chǔ)上實現(xiàn)的Java駐內(nèi)存數(shù)據(jù)網(wǎng)格
一、依賴
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.15.4</version>
</dependency>
二、配置文件
spring:
redis:
database: 7
host: 116.62.178.11
port: 6379
password: 1234qwer
# spring-boot 1.0默認(rèn) jedis; spring-boot2.0 默認(rèn)lettuce ,lettuce線程安全
lettuce:
pool:
# 連接池中的最大空閑連接 默認(rèn)8
max-idle: 8
# 連接池中的最小空閑連接 默認(rèn)0
min-idle: 500
# 連接池最大連接數(shù) 默認(rèn)8 ,負(fù)數(shù)表示沒有限制
max-active: 2000
# 連接池最大阻塞等待時間(使用負(fù)值表示沒有限制) 默認(rèn)-1
max-wait: -1
cache:
type: redis
@Configuration
public class RedissonConfig {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.password}")
private String password;
@Bean(destroyMethod = "shutdown")
RedissonClient redissonClient() throws IOException {
Config config = new Config();
config.useSingleServer()
.setPassword(password)
.setAddress("redis://" + host + ":" + port).setDatabase(7);
return Redisson.create(config);
}
}
三、鎖的使用
讀寫鎖
public class RedissionDemo {
@Autowired
private RedissonClient redissonClient;
@Autowired
private RedisTemplate redisTemplate;
/**
* 讀寫鎖 總結(jié)
*
* 讀鎖又叫共享鎖
* 寫鎖又叫排他鎖(互斥鎖)
* 讀 + 讀 相當(dāng)于無鎖,并發(fā)讀,同時加鎖成功
* 寫 + 寫 阻塞狀態(tài)
* 寫 + 讀 等待寫鎖釋放
* 讀 + 寫 等待讀鎖完,才寫,
*/
public String writeValue() {
String str = "";
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("writeLock");
RLock rLock = readWriteLock.writeLock();
try {
rLock.lock();
str = UUID.randomUUID().toString();
redisTemplate.opsForValue().set("uuid", str);
Thread.sleep(30000);
} catch (Exception e) {
} finally {
rLock.unlock();
}
return str;
}
/**
* 讀鎖
*
* @return
*/
public String readValue() {
String str = "";
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("writeLock");
RLock rLock = readWriteLock.readLock();
rLock.lock();
str = (String) redisTemplate.opsForValue().get("uuid");
rLock.unlock();
return str;
}
}
信號量
public class RedissionDemo {
@Autowired
private RedissonClient redissonClient;
@Autowired
private RedisTemplate redisTemplate;
/**
* 信號量
*
* @return
*/
//停車方法
@GetMapping("/park")
public String park() throws InterruptedException {
//這里是獲取信號量的值,這個信號量的name一定要與你初始化的一致
RSemaphore park = redissonClient.getSemaphore("park");
//這里會將信號量里面的值-1,如果為0則一直等待,直到信號量>0
park.acquire();
//tryAcquire為非阻塞式等待
//park.tryAcquire();
return "ok";
}
public String go() throws InterruptedException {
//這里是獲取信號量的值,這個信號量的name一定要與你初始化的一致
RSemaphore park = redissonClient.getSemaphore("park");
//這里會將信號量里面的值+1,也就是釋放信號量
park.release();
return "ok";
}
}
閉鎖
public class RedissionDemo {
@Autowired
private RedissonClient redissonClient;
@Autowired
private RedisTemplate redisTemplate;
/**
* 閉鎖,限流
*
* @return
* @throws InterruptedException
*/
//鎖門
public String lockdoor() throws InterruptedException {
RCountDownLatch door = redissonClient.getCountDownLatch("door");
//設(shè)置一個班級有20個同學(xué)
door.trySetCount(20);
//需要等到20個同學(xué)全部離開,才鎖門
door.await();
return "鎖門了";
}
public String leave(Long id) throws InterruptedException {
RCountDownLatch door = redissonClient.getCountDownLatch("door");
//表示一個同學(xué)離開
door.countDown();
return "" + id + "號同學(xué)離開了";
}
}
四、分布式秒殺

秒殺流程:

@Service
@Slf4j
public class DistributedSecKillBiz {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private RedissonClient redissonClient;
/**
* 分布式鎖。唯一缺點 枷鎖失效時間
* 枷鎖院子操作,
* 解鎖,刪除鎖也是原子操作 瑕疵沒有續(xù)命
*
* @return
*/
public String doKill() {
String lock = UUID.randomUUID().toString();
String goodsId = "10054";
boolean flag = redisTemplate.opsForValue().setIfAbsent(goodsId, lock, 30, TimeUnit.SECONDS);
if (flag) {
// 獲取鎖成功
try {
Long stock = redisTemplate.opsForValue().decrement(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId);
if (stock > 0) {
redisTemplate.opsForValue().increment(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId);
log.info("扣減庫存成功,還剩:" + stock);
}
return "庫存不足,該商品已搶購?fù)辏?;
} catch (Exception e) {
} finally {
String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(goodsId), lock);
}
}
return doKill();
}
/**
* 整合 redission
* @return
*/
public String doKillDistributed() {
String goodsId = "10054";
RLock lock = redissonClient.getLock(upActivityKey() + SecKillConstant.LOCK + goodsId);
// 獲取鎖成功
try {
//1 阻塞式等待,默認(rèn)30秒時間
//2 自動續(xù)期,如果業(yè)務(wù)超長,續(xù)上新的30秒,不用擔(dān)心過期時間,鎖自動刪除掉
//3 枷鎖的業(yè)務(wù)運行完成,就不會給當(dāng)前的鎖自動續(xù)期,即使沒有手動釋放鎖也會,30秒自動釋放
// lock.lock(30, TimeUnit.SECONDS); //不會自動續(xù)期需要注意
lock.lock();
Long stock = redisTemplate.opsForValue().decrement(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId);
if (stock > 0) {
redisTemplate.opsForValue().increment(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId);
log.info("扣減庫存成功,還剩:" + stock);
}
return "庫存不足,該商品已搶購?fù)辏?;
} catch (Exception e) {
} finally {
lock.unlock();
}
return "fail";
}
/**
* 獲取活動
*
* @return
*/
public ActivityBo upActivity() {
return new ActivityBo("七夕活動", "SEVEN_ACTIVITY", new Date(), new Date());
}
/**
* 活動公共key
*
* @return
*/
public String upActivityKey() {
return SecKillConstant.SEC_KILL + upActivity().getActivityKey() + ":";
}
}
五、redis鎖 單機版可用,分布式用Redisson
package com.yang.yimall.seckill.app.seckill.biz;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* redis 鎖 集群有瑕疵 不能 續(xù)命
*/
@Service
public class RedisLock {
@Autowired
private RedisTemplate redisTemplate;
private String lockName = "lockName";
private ThreadLocal<String> threadLocal = new ThreadLocal<>();
public void lock(String lockName) {
if (tryLock(lockName)) {
return;
}
lock(lockName);
}
public void lock() {
if (tryLock(lockName)) {
return;
}
lock();
}
/**
* 添加key 并且設(shè)置過期時間 原子操作
*
* @param lockName
* @return
*/
public boolean tryLock(String lockName) {
String uuid = UUID.randomUUID().toString();
threadLocal.set(uuid);
return redisTemplate.opsForValue().setIfAbsent(lockName, uuid, 30, TimeUnit.SECONDS);
}
/**
* 如果查詢有key,就刪除, 原子操作
*/
public void unlock() {
String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Collections.singletonList(lockName), threadLocal.get());
}
}
使用
public String doKillUp() {
String goodsId = "10054";
redisLock.lock(goodsId);
// 獲取鎖成功
try {
Long stock = redisTemplate.opsForValue().decrement(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId);
if (stock > 0) {
redisTemplate.opsForValue().increment(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId);
log.info("扣減庫存成功,還剩:" + stock);
}
return "庫存不足,該商品已搶購?fù)辏?;
} catch (Exception e) {
} finally {
redisLock.unlock();
}
return "庫存不足,該商品已搶購?fù)辏?;
}

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
您可能感興趣的文章:
- SpringBoot利用注解來實現(xiàn)Redis分布式鎖
- Spring?Boot?集成Redisson實現(xiàn)分布式鎖詳細(xì)案例
- SpringBoot之使用Redis實現(xiàn)分布式鎖(秒殺系統(tǒng))
- Redis分布式鎖升級版RedLock及SpringBoot實現(xiàn)方法
- SpringBoot整合Redis正確的實現(xiàn)分布式鎖的示例代碼
- SpringBoot使用Redis實現(xiàn)分布式鎖
- SpringBoot使用Redisson實現(xiàn)分布式鎖(秒殺系統(tǒng))
- SpringBoot集成Redisson實現(xiàn)分布式鎖的方法示例
- springboot+redis分布式鎖實現(xiàn)模擬搶單
- 適合 Spring Boot 3.0x的Redis 分布式鎖詳解
相關(guān)文章
Java基于JavaMail實現(xiàn)向QQ郵箱發(fā)送郵件
這篇文章主要為大家詳細(xì)介紹了Java基于JavaMail實現(xiàn)向QQ郵箱發(fā)送郵件的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-01-01
MyBatis查詢數(shù)據(jù)庫語句總結(jié)
MyBatis是一種持久化框架,可以與許多不同類型的關(guān)系型數(shù)據(jù)庫連接,下面這篇文章主要給大家介紹了關(guān)于MyBatis查詢數(shù)據(jù)庫語句的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-06-06
Mybatis注解方式完成輸入?yún)?shù)為list的SQL語句拼接方式
這篇文章主要介紹了Mybatis注解方式完成輸入?yún)?shù)為list的SQL語句拼接方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
Nacos客戶端配置中心緩存動態(tài)更新實現(xiàn)源碼
這篇文章主要為大家介紹了Nacos客戶端配置中心緩存動態(tài)更新實現(xiàn)源碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-03-03
Java和C語言分別實現(xiàn)水仙花數(shù)及拓展代碼
這篇文章主要介紹了分別用Java和C語言實現(xiàn)水仙花數(shù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-11-11

