springboot 集成redission 以及分布式锁的使用详解
作者:介寒食 发布时间:2023-06-20 06:48:43
标签: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;
//方法区
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, "请勿重复提交", false);
}
}finally{
rLock.unlock();
}
private void redissonDoc() throws InterruptedException {
//1. 普通的可重入锁
RLock lock = redissonClient.getLock("generalLock");
// 拿锁失败时会不停的重试
// 具有Watch Dog 自动延期机制 默认续30s 每隔30/3=10 秒续到30s
lock.lock();
// 尝试拿锁10s后停止重试,返回false
// 具有Watch Dog 自动延期机制 默认续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的基础上实现的Java驻内存数据网格
一、依赖
<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默认 jedis; spring-boot2.0 默认lettuce ,lettuce线程安全
lettuce:
pool:
# 连接池中的最大空闲连接 默认8
max-idle: 8
# 连接池中的最小空闲连接 默认0
min-idle: 500
# 连接池最大连接数 默认8 ,负数表示没有限制
max-active: 2000
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认-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;
/**
* 读写锁 总结
*
* 读锁又叫共享锁
* 写锁又叫排他锁(互斥锁)
* 读 + 读 相当于无锁,并发读,同时加锁成功
* 写 + 写 阻塞状态
* 写 + 读 等待写锁释放
* 读 + 写 等待读锁完,才写,
*/
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");
//设置一个班级有20个同学
door.trySetCount(20);
//需要等到20个同学全部离开,才锁门
door.await();
return "锁门了";
}
public String leave(Long id) throws InterruptedException {
RCountDownLatch door = redissonClient.getCountDownLatch("door");
//表示一个同学离开
door.countDown();
return "" + id + "号同学离开了";
}
}
四、分布式秒杀
秒杀流程:
@Service
@Slf4j
public class DistributedSecKillBiz {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private RedissonClient redissonClient;
/**
* 分布式锁。唯一缺点 枷锁失效时间
* 枷锁院子操作,
* 解锁,删除锁也是原子操作 瑕疵没有续命
*
* @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 "库存不足,该商品已抢购完!";
} 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 阻塞式等待,默认30秒时间
//2 自动续期,如果业务超长,续上新的30秒,不用担心过期时间,锁自动删除掉
//3 枷锁的业务运行完成,就不会给当前的锁自动续期,即使没有手动释放锁也会,30秒自动释放
// lock.lock(30, TimeUnit.SECONDS); //不会自动续期需要注意
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 "库存不足,该商品已抢购完!";
} 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 锁 集群有瑕疵 不能 续命
*/
@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 并且设置过期时间 原子操作
*
* @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 "库存不足,该商品已抢购完!";
} catch (Exception e) {
} finally {
redisLock.unlock();
}
return "库存不足,该商品已抢购完!";
}
来源:https://www.cnblogs.com/jiehanshi/p/13693129.html


猜你喜欢
- requestFoucs();无效。requestFoucsFromTouch();无效。webview.setTouchListener;
- 1. 什么是JWTJSON Web Token(JWT)是一个轻量级的认证规范,这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信
- 前言同C语言一样,Java也有断言关键字assert,它们的用法也比较相似。注意:Java的断言是从1.4版本开始的,以前的版本不支持断言。
- 从Handler.post()说起Handler.post()是用来发送消息的,我们看下Handler源码的处理:public final
- 注解@Validated和BindingResult对入参非空校验在项目当中少不了入参校验,服务器和浏览器互不信任,不能因为前端加入参判断了
- //写注册表RegistryKey regWrite;//往HKEY_CURRENT_USER主键里的Software子键下写一个名为“Te
- Swing 的组件与AWT 组件相似,但又为每一个组件增添了新的方法,并提供了更多的高级组件.Swing 的基本组件:1.按钮(JButto
- 本文实例为大家分享了java实现TCP聊天程序的具体代码,供大家参考,具体内容如下服务端代码:package com.test.server
- 本篇和大家分享的是通过maven对springboot中打war包和jar包;war通常来说生成后直接放到tomcat的webapps下面就
- 一、新建BeanUtil类import lombok.extern.slf4j.Slf4j;import org.springframewo
- //执行顺序:(优先级从高到低。)静态代码块>mian方法>构造代码块>构造方法。其中静态代码块只执行一次。构造代码块在每
- 一、背景目前的Springboot,当发生了任何修改之后,必须关闭后再启动Application类才能够生效,显得略微麻烦。Springbo
- 介绍无论是在WPF中还是WinForm中,都有用户控件(UserControl)和自定义控件(CustomControl),这两种控件都是对
- *注:可以用 adb logcat > 路径/文件名 来保存,此命令执行之时起的全部日志信息到一个文件里,ctrl + C 结束日志输
- 问题提出:自己在做一个小网站充当练手,但是前端图片经过base64加密后传往后端在解码。但是一直都有问题,请大神赐教 publi
- 最近在做保证金余额查询优化,在项目启动时候需要把余额全量加载到本地缓存,因为需要全量查询所有骑手的保证金余额,为了不影响主数据库的性能,考虑
- 前言工作中是否有这样的场景,多个线程任务,如果所有线程完成到某个阶段,你希望知道所有线程均完成该阶段。当然你使用线程计数可以实现,只是不够优
- 众所周知,当你点击一个超链接进行跳转时,WebView会自动将当前地址作为Referer(引荐)发给服务器,因此很多服务器端程序通过是否包含
- C# 匿名函数、lambda表达式、Linq查询一、匿名函数的使用匿名函数是一个“内联”语句或表达式
- 使用linq把多列的List转化为只有指定列的List1、方式一2、方式二