Java锁&Redisson分布式锁的实现
Redis分布式锁的实现1.分布式锁介绍场景1.分布式锁介绍场景分布式锁的场景在现物联网平台是一个极其多的一个技术,比如商品秒杀、优惠券抢购、偶尔出的一些小活动等等。只要是一个分布式的架构中,基本上都会接触到分布式锁的运用...
Redis分布式锁的实现
1.锁
Java锁
- 乐观锁 悲观锁
- 读锁(共享锁) 写锁(排它锁)
- 自旋锁 非自旋锁
- 无锁 偏向锁 轻量级锁 重量级锁
- 分布式锁
- 区间锁
- 重入锁 非重入锁
- 公平锁 非公平锁
悲观锁与乐观锁
悲观锁:
悲观锁顾名思义来解析就是很悲观,认为自己在使用数据的时候一定会有其他的线程来修改数据。所以它会在这之前就先加锁来确保数据不被其他线程修改
实现方式:
(1)内置synchronized
(2)Lock
实用场景:写操作比较多 可以先加锁拉来确保数据的正确性
乐观锁:
乐观锁与悲观锁相反,认为自己在使用数据的时候不会有其他的线程来修改数据。所以不会添加锁,只是在更新数据的同时去判断一下是否之前有其他线程更新过
实现方式:
(1)CAS算法
实用场景:读操作比较多的时候 不加锁的特点是能够让读操作性能得到一定提升
synchronized使用
使用方式:
- 同步实例方法,锁是当前实例对象
- 同步类方法,锁是当前类对象
- 同步代码块,锁是括号里的对象
实现方式:
synchronized是jvm的内置锁,通过内部对象Monitor(监听器锁)实现。
示例
package com.example.demo.test;
public class Lock {
private final static Object object = new Object();
public static void lock(){
synchronized (object){
System.out.println("我是第一个同步块");
System.out.println("我是第二个同步块");
}
}
public static void main(String[] args) {
lock();
}
}
2.分布式锁
介绍
分布式锁的场景在现物联网平台是一个极其多的一个技术,比如商品秒杀、优惠券抢购、偶尔出的一些小活动等等。只要是一个分布式的架构中,基本上都会接触到分布式锁的运用
运用(单体架构vs分布式架构)
首先 我们先来看一段简单的代码 解决问题先发现问题
package com.example.demo.test;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value = "test")
public class DistributedLock {
@GetMapping(value = "lock")
public Object lockProduct(){
//这里假设我是存库 我这个商品有50个库存
int stock = 50;
if (stock>=1){
int num = stock - 1;
System.out.println("商品售卖成功!"+num);
return "true";
}else{
System.out.println("商品售卖失败!");
return "false";
}
}
}
上面的代码会出现当并发高时 比如我两个或者多个都同时执行了stock-1的这一行代码 那么都是获取的50库存数,假如我库存到最后只剩下一个库存时 这时候就会出现超卖的情况 这时候可以使用加锁来解决 也就是上面介绍的synchronized方法来同步代码块
public Object lockProduct(){
synchronized (this){
int stock = 50;
if (stock>=1){
int num = stock - 1;
System.out.println("商品售卖成功!"+num);
return "true";
}else{
System.out.println("商品售卖失败!");
return "false";
}
}
}
在单机的环境下上面这个是没问题 但是在分布式环境下并发高还是会出现超卖的情况,解决方式当然是使用分布式锁 也就是下面要说的 这里我们使用redis作为解决方案,具体实如下
package com.example.demo.test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping(value = "test")
public class DistributedLock {
@Autowired
private StringRedisTemplate template;
@GetMapping(value = "lock")
public Object lockProduct(){
//模拟一个商品
Product product = new Product("10001","一双鞋子",499.00);
//设置一个UUID来作为每一个线程的唯一标识
String uuid = UUID.randomUUID().toString();
try {
//将商品sku存储redis
Boolean flag = template.opsForValue().setIfAbsent(product.getSku(),
uuid,
20,
TimeUnit.SECONDS);
if (!flag){
return "false";
}
int shoes_num = Integer.valueOf(template.opsForValue().get("shoes"));
if (shoes_num>0){
int stock = shoes_num - 1;
template.opsForValue().set("shoes", String.valueOf(stock));
System.out.println("商品售卖成功!"+stock);
}else{
System.out.println("商品售卖失败!");
return "false;";
}
}finally {
if (uuid.equals(template.opsForValue().get(product.getSku()))){
template.delete(product.getSku());
}
}
return "true";
}
}
但是在上面还是会出现一个问题,接口请求会有一个时间。我在上面设置redis的失效时间为20s,假如我的接口请求时间超过30秒那么就会出现我的key找不到了 来解决这个问题的办法很简单 我们可以用redisson框架 使用方式也很简单 直接引入redisson依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.6.5</version>
</dependency>
写一个配置类配置一下redisson
package com.example.demo.config;
import org.redisson.Redisson;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfiguration {
@Bean
public Redisson redisson(){
Config config = new Config();
//下面为单机模式 其他模式可以亲自尝试
config.useSingleServer().setAddress("redis://localhost:6379").setDatabase(0);
return (Redisson) Redisson.create();
}
}
接下来就简单了 看下面代码
package com.example.demo.test;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping(value = "test")
public class DistributedLock {
@Autowired
private StringRedisTemplate template;
@Autowired
private Redisson redisson;
@GetMapping(value = "lock")
public Object lockProduct(){
//模拟一个商品
Product product = new Product("10001","一双鞋子",499.00);
RLock redissonLock = redisson.getLock(product.getSku());
try {
redissonLock.lock();;
int shoes_num = Integer.valueOf(template.opsForValue().get("shoes"));
if (shoes_num>0){
int stock = shoes_num - 1;
template.opsForValue().set("shoes", String.valueOf(stock));
System.out.println("商品售卖成功!"+stock);
}else{
System.out.println("商品售卖失败!");
return "false;";
}
}finally {
redissonLock.unlock();
}
return "true";
}
}
只需要引入Redisson就可以直接一次解决锁的配置与释放,对开发解决了成本与开发时遇到的问题
好了,这期说到这。很久没写blog了,之前因一直项目原因时间紧凑。接下来会在写blog的这条路上矜持不谢 感谢观看
尝试 或许可以成功 但一定不会遗憾!
更多推荐
所有评论(0)