分布式锁的实现
- 需要注意的点
1、锁不安全:
任意客户端获得锁后,在释放锁之前该服务一旦崩溃宕机,锁将永远都不会释放简单来讲:其他服务都将竞争不到锁,竞争不到锁的线程在不断自旋,与此同时还会不断有新的请求接入,最终导致服务宕机,Redis宕机
2、锁不失效:
假设过期时间10S,服务A可能在10S内还没搞完业务逻辑,锁超时后服务B获得了锁,这时服务A执行完了业务逻辑,这样错误的释放了由服务B加的锁,就会导致锁失效。
3、锁超时:
太长的话 如果某个服务宕机后,其他服务需要等待很久才能获得锁 就没有必要
太短的话 可能大部分服务还没有执行完业务逻辑,锁还来不及释放就过期了,产生并发安全问题
- 需要满足的条件
1、互斥性:在任何时候,只有一个客户端可以获得锁
2、防止死锁:即使获得锁的客户端在没有释放锁之前崩溃宕机,后续客户端继续可以竞争到锁
3、防止误解别人的锁,或者是自己还需要用的锁:释放锁的客户端一定是加锁的客户端,不能错误的释放其他客户端加的锁
开始准备工作:
添加RedisLock配置类 定义方法
@Component
public class RedisLock {
@Autowired
private RedisTemplate redisTemplate; //定义redis模板
//线程安全的map
private ConcurrentMap<String, String> keyMap = new ConcurrentHashMap<>(16);
//守护线程map
private ConcurrentMap<String, ContinueThread> threadMap = new ConcurrentHashMap<>(16);
//加锁
public String addLock(String key) {
ValueOperations<String, Object> kv = redisTemplate.opsForValue();
String valueSign = UUID.randomUUID().toString();
//锁超时10s
//10s后未释放锁,其他线程可以竞争
while (!kv.setIfAbsent(key, valueSign, 10, TimeUnit.SECONDS)) {
//竞争锁失败的话,就暂时让出CPU资源 休息一下
Thread.yield();
}
//put 竞争锁成功
keyMap.put(key, valueSign);
//开启守护线程
ContinueThread continueThread = new ContinueThread(key, valueSign);
continueThread.setDaemon(true);
//put 守护线程成功
threadMap.put(key, continueThread);
continueThread.start();
return valueSign; //返回value
}
//解锁
public void unLock(String key, String value) {
String keyLock = keyMap.get(key);
System.out.println("开始解锁=============");
System.out.println("解锁key:" + key);
System.out.println("解锁keyLock:" + keyLock);
System.out.println("解锁value:" + value);
if (!value.equals(keyLock)) return; //这个时候出去 是因为不是我自己的锁 所以不释放 不要把别人的锁释放掉了
redisTemplate.delete(key); //是我加的锁,可以释放
//这个时候就可以把守护线程关掉了
ContinueThread thread = threadMap.get(key);
System.out.println("解锁thread:" + thread);
//判断一下 然后关掉 不要直接关掉 严谨一点
Optional.ofNullable(thread).ifPresent(t1 -> {
t1.stopT();
});
System.out.println("结束解锁=============");
}
//续命 内部类 守护线程
private class ContinueThread extends Thread {
private boolean flag = false; //定义一个开关
private String key;
private String value;
public ContinueThread(String key, String value) {
this.key = key;
this.value = value;
}
//调用这个关掉线程
public void stopT() {
this.flag = true;
}
@Override
public void run() {
while (!flag) {
System.err.println("isInterrupted:" + !isInterrupted());
try {
//三秒
Thread.sleep(3 * 1000);
} catch (InterruptedException i) {
System.err.println("这个时候在续命了.....");
redisTemplate.opsForValue().set(key, value, 10, TimeUnit.SECONDS);
}
}
}
}
}
一、 注入RedisLock依赖
@Autowired
private RedisLock redisLock;
二、这里自定义变量
private static final String KEY= "key";
private static final String LOCK = "lock";
三、 为了方便配置初始化redis数据
@PostConstruct
public void init() {
redisTemplate.delete(KEY);
redisTemplate.opsForValue().set(KEY, String.valueOf(20));
}
代码实现
public void redisLock() {
//拿到返回的value
String addLock = redisLock.addLock(LOCK);
System.out.println("返回的sign:" + addLock);
Integer number;
//加锁
number = Integer.parseInt(redisTemplate.opsForValue().get(KEY).toString());
System.out.println("number:" + number);
//小于等于0 这个时候没了
if (number <= 0) return;
number = new BigDecimal(redisTemplate.opsForValue().decrement(KEY).toString()).intValue();
System.out.println("成功,剩余:" + number);
redisLock.unLock(LOCK, addLock);
System.out.println("去掉key:" + KEY);
System.out.println("执行完毕");
}
使用Jemter模拟环境,最后贴出运行结果
加锁keyMap:[lock=70ff64b1-9332-49bc-a34d-d947a1d03af3]
加锁threadMap:[lock=Thread[Thread-19,5,main]]
返回的sign:70ff64b1-9332-49bc-a34d-d947a1d03af3
number:20
成功,剩余:19
开始解锁=============
解锁key:lock
解锁keyLock:70ff64b1-9332-49bc-a34d-d947a1d03af3
解锁value:70ff64b1-9332-49bc-a34d-d947a1d03af3
解锁thread:Thread[Thread-19,5,main]
结束解锁=============
去掉key:hy
执行完毕
isInterrupted:true
加锁keyMap:[lock=05289b4d-d21e-4bc7-93e3-419483fc952f]
加锁threadMap:[lock=Thread[Thread-20,5,main]]
返回的sign:05289b4d-d21e-4bc7-93e3-419483fc952f
number:19
成功,剩余:18
开始解锁=============
解锁key:lock
解锁keyLock:05289b4d-d21e-4bc7-93e3-419483fc952f
解锁value:05289b4d-d21e-4bc7-93e3-419483fc952f
解锁thread:Thread[Thread-20,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=a13724bc-9b44-4139-89a2-c9b7a202fdc5]
加锁threadMap:[lock=Thread[Thread-21,5,main]]
返回的sign:a13724bc-9b44-4139-89a2-c9b7a202fdc5
number:18
成功,剩余:17
开始解锁=============
解锁key:lock
解锁keyLock:a13724bc-9b44-4139-89a2-c9b7a202fdc5
解锁value:a13724bc-9b44-4139-89a2-c9b7a202fdc5
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
解锁thread:Thread[Thread-21,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=c5cf5452-12f0-4bd5-ada1-39fae37d208d]
加锁threadMap:[lock=Thread[Thread-22,5,main]]
返回的sign:c5cf5452-12f0-4bd5-ada1-39fae37d208d
number:17
成功,剩余:16
开始解锁=============
解锁key:lock
解锁keyLock:c5cf5452-12f0-4bd5-ada1-39fae37d208d
解锁value:c5cf5452-12f0-4bd5-ada1-39fae37d208d
解锁thread:Thread[Thread-22,5,main]
结束解锁=============
去掉key:hy
加锁keyMap:[lock=bb260223-4a29-4525-91a4-5b0a95ea1edb]
执行完毕
加锁threadMap:[lock=Thread[Thread-23,5,main]]
返回的sign:bb260223-4a29-4525-91a4-5b0a95ea1edb
number:16
成功,剩余:15
开始解锁=============
解锁key:lock
解锁keyLock:bb260223-4a29-4525-91a4-5b0a95ea1edb
解锁value:bb260223-4a29-4525-91a4-5b0a95ea1edb
解锁thread:Thread[Thread-23,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=32fdc719-0018-4b70-82c8-17c90ab241d0]
加锁threadMap:[lock=Thread[Thread-24,5,main]]
返回的sign:32fdc719-0018-4b70-82c8-17c90ab241d0
number:15
成功,剩余:14
开始解锁=============
解锁key:lock
解锁keyLock:32fdc719-0018-4b70-82c8-17c90ab241d0
解锁value:32fdc719-0018-4b70-82c8-17c90ab241d0
解锁thread:Thread[Thread-24,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=b916d222-a416-4585-8b14-1e97d7dfd235]
加锁threadMap:[lock=Thread[Thread-25,5,main]]
返回的sign:b916d222-a416-4585-8b14-1e97d7dfd235
number:14
成功,剩余:13
开始解锁=============
解锁key:lock
解锁keyLock:b916d222-a416-4585-8b14-1e97d7dfd235
解锁value:b916d222-a416-4585-8b14-1e97d7dfd235
解锁thread:Thread[Thread-25,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=a0267cee-c106-412f-8ee0-5da4f7df2370]
加锁threadMap:[lock=Thread[Thread-26,5,main]]
返回的sign:a0267cee-c106-412f-8ee0-5da4f7df2370
number:13
成功,剩余:12
开始解锁=============
解锁key:lock
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
解锁keyLock:a0267cee-c106-412f-8ee0-5da4f7df2370
解锁value:a0267cee-c106-412f-8ee0-5da4f7df2370
解锁thread:Thread[Thread-26,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=3ce70e99-d94d-41aa-a3d7-6f4469c29fc5]
加锁threadMap:[lock=Thread[Thread-27,5,main]]
返回的sign:3ce70e99-d94d-41aa-a3d7-6f4469c29fc5
number:12
成功,剩余:11
开始解锁=============
解锁key:lock
解锁keyLock:3ce70e99-d94d-41aa-a3d7-6f4469c29fc5
解锁value:3ce70e99-d94d-41aa-a3d7-6f4469c29fc5
解锁thread:Thread[Thread-27,5,main]
结束解锁=============
去掉key:hy
加锁keyMap:[lock=3bf1c5da-807e-4428-8f9f-6f7e3d9e9858]
执行完毕
加锁threadMap:[lock=Thread[Thread-28,5,main]]
返回的sign:3bf1c5da-807e-4428-8f9f-6f7e3d9e9858
number:11
成功,剩余:10
开始解锁=============
解锁key:lock
解锁keyLock:3bf1c5da-807e-4428-8f9f-6f7e3d9e9858
解锁value:3bf1c5da-807e-4428-8f9f-6f7e3d9e9858
解锁thread:Thread[Thread-28,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=5b9876a4-223c-4661-8330-7edc4afa00de]
加锁threadMap:[lock=Thread[Thread-29,5,main]]
返回的sign:5b9876a4-223c-4661-8330-7edc4afa00de
number:10
成功,剩余:9
开始解锁=============
解锁key:lock
解锁keyLock:5b9876a4-223c-4661-8330-7edc4afa00de
解锁value:5b9876a4-223c-4661-8330-7edc4afa00de
解锁thread:Thread[Thread-29,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=55dea574-9c73-4f5e-b33c-42079a219fab]
加锁threadMap:[lock=Thread[Thread-30,5,main]]
返回的sign:55dea574-9c73-4f5e-b33c-42079a219fab
number:9
成功,剩余:8
开始解锁=============
解锁key:lock
解锁keyLock:55dea574-9c73-4f5e-b33c-42079a219fab
解锁value:55dea574-9c73-4f5e-b33c-42079a219fab
加锁keyMap:[lock=8bb8cc39-e94d-49ee-9fd4-f3efd827547b]
解锁thread:Thread[Thread-30,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁threadMap:[lock=Thread[Thread-31,5,main]]
返回的sign:8bb8cc39-e94d-49ee-9fd4-f3efd827547b
number:8
成功,剩余:7
开始解锁=============
解锁key:lock
解锁keyLock:8bb8cc39-e94d-49ee-9fd4-f3efd827547b
解锁value:8bb8cc39-e94d-49ee-9fd4-f3efd827547b
解锁thread:Thread[Thread-31,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=c5d91c6a-dfd8-481c-9dfa-a9b42d3ba522]
加锁threadMap:[lock=Thread[Thread-32,5,main]]
返回的sign:c5d91c6a-dfd8-481c-9dfa-a9b42d3ba522
number:7
成功,剩余:6
开始解锁=============
解锁key:lock
解锁keyLock:c5d91c6a-dfd8-481c-9dfa-a9b42d3ba522
解锁value:c5d91c6a-dfd8-481c-9dfa-a9b42d3ba522
解锁thread:Thread[Thread-32,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=aaf0c8e1-08f8-4e1f-b59f-e3ebca1c0c78]
加锁threadMap:[lock=Thread[Thread-33,5,main]]
返回的sign:aaf0c8e1-08f8-4e1f-b59f-e3ebca1c0c78
number:6
成功,剩余:5
开始解锁=============
解锁key:lock
解锁keyLock:aaf0c8e1-08f8-4e1f-b59f-e3ebca1c0c78
解锁value:aaf0c8e1-08f8-4e1f-b59f-e3ebca1c0c78
解锁thread:Thread[Thread-33,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=9a924011-c9e7-4954-a3a6-6621937db0e8]
加锁threadMap:[lock=Thread[Thread-34,5,main]]
返回的sign:9a924011-c9e7-4954-a3a6-6621937db0e8
number:5
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
成功,剩余:4
开始解锁=============
解锁key:lock
解锁keyLock:9a924011-c9e7-4954-a3a6-6621937db0e8
解锁value:9a924011-c9e7-4954-a3a6-6621937db0e8
解锁thread:Thread[Thread-34,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=f1da0948-1c2c-45a9-b546-f4774307a1eb]
加锁threadMap:[lock=Thread[Thread-35,5,main]]
返回的sign:f1da0948-1c2c-45a9-b546-f4774307a1eb
number:4
成功,剩余:3
开始解锁=============
解锁key:lock
解锁keyLock:f1da0948-1c2c-45a9-b546-f4774307a1eb
解锁value:f1da0948-1c2c-45a9-b546-f4774307a1eb
解锁thread:Thread[Thread-35,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=9857d21c-def8-4ab8-8e31-42fd52ef2a7f]
加锁threadMap:[lock=Thread[Thread-36,5,main]]
返回的sign:9857d21c-def8-4ab8-8e31-42fd52ef2a7f
number:3
成功,剩余:2
开始解锁=============
解锁key:lock
解锁keyLock:9857d21c-def8-4ab8-8e31-42fd52ef2a7f
解锁value:9857d21c-def8-4ab8-8e31-42fd52ef2a7f
解锁thread:Thread[Thread-36,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=e323948f-85ac-45f3-a707-76754a1cd7cb]
加锁threadMap:[lock=Thread[Thread-37,5,main]]
返回的sign:e323948f-85ac-45f3-a707-76754a1cd7cb
number:2
成功,剩余:1
开始解锁=============
解锁key:lock
解锁keyLock:e323948f-85ac-45f3-a707-76754a1cd7cb
解锁value:e323948f-85ac-45f3-a707-76754a1cd7cb
解锁thread:Thread[Thread-37,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=9a0a3219-55a6-4d96-945c-510984937fbe]
加锁threadMap:[lock=Thread[Thread-38,5,main]]
返回的sign:9a0a3219-55a6-4d96-945c-510984937fbe
number:1
成功,剩余:0
开始解锁=============
解锁key:lock
解锁keyLock:9a0a3219-55a6-4d96-945c-510984937fbe
解锁value:9a0a3219-55a6-4d96-945c-510984937fbe
解锁thread:Thread[Thread-38,5,main]
结束解锁=============
去掉key:hy
执行完毕
加锁keyMap:[lock=1384f3a2-8d5e-43be-968e-2fc2d25d4826]
加锁threadMap:[lock=Thread[Thread-39,5,main]]
返回的sign:1384f3a2-8d5e-43be-968e-2fc2d25d4826
number:0
isInterrupted:true
小于等于0 这个时候没了
isInterrupted:true
isInterrupted:true
isInterrupted:true
加锁keyMap:[lock=7e4dae38-2a4c-4ee3-a4b8-0439389eb793]
加锁threadMap:[lock=Thread[Thread-40,5,main]]
返回的sign:7e4dae38-2a4c-4ee3-a4b8-0439389eb793
number:0
isInterrupted:true
小于等于0 这个时候没了
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
isInterrupted:true
加锁keyMap:[lock=9fce46b9-6b1e-4061-a6ad-9d3c0642b401]
加锁threadMap:[lock=Thread[Thread-41,5,main]]
返回的sign:9fce46b9-6b1e-4061-a6ad-9d3c0642b401
number:0
isInterrupted:true
小于等于0 这个时候没了
isInterrupted:true