一、分布式锁应用场景介绍
随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题!
分布式锁主流的实现方案:
每一种分布式锁解决方案都有各自的优缺点:
这里,我们就基于redis实现分布式锁。
二、Redis分布式锁第一版
拿到锁后的操作可能存在延迟.
锁过期,后续释放的是别人的锁
//第一版:拿到锁后的操作可能存在延迟,锁过期。后续释放的是别人的锁
@RequestMapping(value = "/doLock1")
public String doLock1(){
String result;
// 1.获取锁
Boolean islocked = redistemplate.opsForValue().setIfAbsent("lock", "lock",50, TimeUnit.MILLISECONDS);
// 2.判断是否成功获得锁
// 3.成功:则进行操作
if(islocked){
// 3.1 进行操作
Object num = redistemplate.opsForValue().get("num");
if (StringUtils.isEmpty(num)){
return "操作失败:key不存在!";
}else{
result = String.valueOf(redistemplate.opsForValue().increment("num"));
}
//***************************可能存在延迟,key过期。释放的是别人的锁****************************************
// 3.2 释放锁
redistemplate.delete("lock");
return result;
}else{
try {
TimeUnit.SECONDS.sleep(1);
// 4.失败:则继续尝试获取
return doLock1();
} catch (InterruptedException e) {
e.printstacktrace();
return String.valueOf(e);
}
}
}
三、Redis分布式锁第二版
//第二版:UUID,判断与删除不是原子操作,仍然存在释放别人锁的情况
@RequestMapping(value = "/doLock2")
public String doLock2(){
String result;
//生成uuid,用于辨别锁的拥有者
String uuid= String.valueOf(UUID.randomUUID());
// 1.获取锁
Boolean islocked = redistemplate.opsForValue().setIfAbsent("lock", uuid,50, TimeUnit.MILLISECONDS);
// 2.判断是否成功获得锁
// 3.成功:则进行操作
if(islocked){
// 3.1 进行操作
Object num = redistemplate.opsForValue().get("num");
if (StringUtils.isEmpty(num)){
return "操作失败:key不存在!";
}else{
result = String.valueOf(redistemplate.opsForValue().increment("num"));
}
// 3.2 释放锁
//判断当前锁是否是自己的
//******************************判断与删除不是原子操作,仍然存在释放别人锁的情况******************************
if(uuid.equals(redistemplate.opsForValue().get("lock"))){
redistemplate.delete("lock");
}
return result;
}else{
try {
TimeUnit.SECONDS.sleep(1);
// 4.失败:则继续尝试获取
return doLock1();
} catch (InterruptedException e) {
e.printstacktrace();
return String.valueOf(e);
}
}
}
四、Redis分布式锁第三版
//第三版:LUA脚本,包含判断与删除的逻辑,使其成为原子操作
@RequestMapping(value = "/doLock3")
public String doLock3(){
String result;
//生成uuid,用于辨别锁的拥有者
String uuid= String.valueOf(UUID.randomUUID());
//定义脚本对象
DefaultRedisScript scriptObject=new DefaultRedisScript();
scriptObject.setResultType(Long.class);
String script="if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
scriptObject.setScriptText(script);
// 1.获取锁
Boolean islocked = redistemplate.opsForValue().setIfAbsent("lock", uuid,3, TimeUnit.SECONDS);
// 2.判断是否成功获得锁
// 3.成功:则进行操作
if(islocked){
// 3.1 进行操作
Object num = redistemplate.opsForValue().get("num");
if (StringUtils.isEmpty(num)){
return "操作失败:key不存在!";
}else{
result = String.valueOf(redistemplate.opsForValue().increment("num"));
}
// 3.2 释放锁:使用LUA脚本 判断并删除锁
//返回1:删除成功 返回0:已过期
//KEYS[1] :Arrays.asList("lock") ARGV[1]:uuid
redistemplate.execute(scriptObject, Arrays.asList("lock"), uuid);
return result;
}else{
try {
TimeUnit.SECONDS.sleep(1);
// 4.失败:则继续尝试获取
return doLock1();
} catch (InterruptedException e) {
e.printstacktrace();
return String.valueOf(e);
}
}
}
原文地址:https://www.jb51.cc/wenti/3281958.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。