Redis分布式锁的实现
- 1.锁
- Java锁
- 悲观锁与乐观锁
- synchronized使用
- 2.分布式锁
- 介绍
- 运用(单体架构vs分布式架构)
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的这条路上矜持不谢 感谢观看
尝试 或许可以成功 但一定不会遗憾!