基于Redis的分布式锁两种实现方式
最近有一个竞拍的项目会用到分布式锁,网上查到的结果是有三种途径可以实现。1.数据库锁机制,2.redis的锁,3.zookeeper。考虑到使用mysql实现会在性能这一块会受影响,zookeeper又是不怎么会。所以使用redis来实现了。
第一种:使用redis的watch命令进行实现

如上图所示:session1在执行修改之前使用watch命令监视了age,然后又在session2更新了age之后,session1在执行exec,在该命令执行的时候应该会检查age值是否更改,现在是已经发生了改变,所以返回执行失败。
基于上述图示写了一段java代码
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction; import java.util.List; public class RedisWatchTest extends Thread { private String auctionCode;
public RedisWatchTest
(String auctionCode) {
super(auctionCode);
this.auctionCode = auctionCode;
}
private static int bidPrice = 100; public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + "主线程运行开始!");
//更改key为a的值
Jedis jedis=new Jedis("127.0.0.1",6379);
jedis.set("goodsprice","0");
System.out.println("输出初始化值:"+jedis.get("goodsprice"));
jedis.close();
RedisWatchTest thread1 = new RedisWatchTest("A001");
RedisWatchTest thread2 = new RedisWatchTest("B001");
thread1.start();
thread2.start();
try{
thread1.join();
thread2.join();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "主线程运行结束!");
} @Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程运行开始 ");
Jedis jedis=new Jedis("127.0.0.1",6379);
try {
if(Thread.currentThread().getName()=="B001"){
sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
//监视KEY
jedis.watch("goodsprice");
//A先进
String v = jedis.get("goodsprice");
Integer iv = Integer.valueOf(v);
//条件都给过
if(bidPrice > iv){
Transaction tx = jedis.multi();// 开启事务
Integer bp = iv + 100;
//出价成功,事务未提交
tx.set("goodsprice",String.valueOf(bp));
System.out.println("子线程" + auctionCode + "set成功");
try {
if(Thread.currentThread().getName()=="A001"){
sleep(2000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
List<Object> list = tx.exec();
if (list == null ||list.size()==0) {
System.out.println("子线程" + auctionCode + ",出价失败");
}else{
System.out.println("子线程"+this.auctionCode +", 出价:"+ jedis.get("goodsprice") +",出价时间:"
+ System.nanoTime());
}
}else{
System.out.println("出价低于现有价格!");
}
jedis.close();
System.out.println(Thread.currentThread().getName() + "线程运行结束");
} }
执行结果:
main主线程运行开始!
输出初始化值:0
B001线程运行开始
A001线程运行开始
子线程A001set成功
子线程B001set成功
子线程B001, 出价:100,出价时间:76269463819581
B001线程运行结束
子线程A001,出价失败
A001线程运行结束
main主线程运行结束!
上述代码是在主线程里面开了两个子线程,首先让B001先等待1s时间,让A001先watch最高价,然后在A001事务exec之前让他等待2s时间。这个时候B001已经出价成功了,所以最后应当返回A001出价失败。
第二种:使用redis的setnx命令进行实现
关于setnx的详解参考的是下面这个文章,拿了他的两个加锁和解锁的正确实现方式。
https://www.cnblogs.com/linjiqin/p/8003838.html下面是通过setnx实现的相关代码
import redis.clients.jedis.Jedis; import java.util.Collections; /**
* @author chen
* @date 2018/4/30 16:09
*/
public class RedisSetNXTest extends Thread{ private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX"; private String auctionCode;
public RedisSetNXTest
(String auctionCode) {
super(auctionCode);
this.auctionCode = auctionCode;
}
private static int bidPrice = 100; public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + "主线程运行开始!");
//更改key为a的值
Jedis jedis=new Jedis("127.0.0.1",6379);
jedis.set("goodsprice","0");
System.out.println("输出初始化值:"+jedis.get("goodsprice"));
jedis.close();
RedisSetNXTest thread1 = new RedisSetNXTest("A001");
RedisSetNXTest thread2 = new RedisSetNXTest("B001");
thread1.start();
thread2.start();
try{
thread1.join();
thread2.join();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "主线程运行结束!");
} @Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程运行开始 ");
Jedis jedis=new Jedis("127.0.0.1",6379);
try {
if(Thread.currentThread().getName()=="B001"){
sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
//让A先拿到锁
boolean isOk= tryGetDistributedLock(jedis, "goods_lock", Thread.currentThread().getName() , 10000); try {
if(Thread.currentThread().getName()=="A001"){
sleep(2000);
}
} catch (InterruptedException e) {
e.printStackTrace();
} if(isOk) {
System.out.println("子线程"+this.auctionCode +"拿到锁");
String v = jedis.get("goodsprice");
Integer iv = Integer.valueOf(v);
//条件都给过
if(bidPrice > iv){ Integer bp = iv + 100;
//出价成功,事务未提交
jedis.set("goodsprice",String.valueOf(bp));
System.out.println("子线程"+this.auctionCode +", 出价:"+ jedis.get("goodsprice") +",出价时间:"
+ System.nanoTime()); }else{
System.out.println("出价低于现有价格!");
}
boolean isOk1= releaseDistributedLock(jedis, "goods_lock", Thread.currentThread().getName());
if(isOk1){
System.out.println("子线程"+this.auctionCode +"释放锁");
} }else{ System.out.println("子线程" + auctionCode + "未拿到锁");
}
jedis.close();
System.out.println(Thread.currentThread().getName() + "线程运行结束");
}
/**
* 尝试获取分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @param expireTime 超期时间
* @return 是否获取成功
*/
public boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) { String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime); if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false; } private static final Long RELEASE_SUCCESS = 1L; /**
* 释放分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @return 是否释放成功
*/
public boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) { String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1])
else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections
.singletonList(requestId)); if (RELEASE_SUCCESS.equals(result)) {
return true;
}
return false; }
}
执行结果:
main主线程运行开始!
输出初始化值:0
A001线程运行开始
B001线程运行开始
子线程B001未拿到锁
B001线程运行结束
子线程A001拿到锁
子线程A001, 出价:100,出价时间:77389730033100
子线程A001释放锁
A001线程运行结束
main主线程运行结束!
同样代码是在主线程里面开了两个子线程,先让B001等待,让A001先去拿到锁。然后让B001在没有拿到锁的情况下去操作redis,代码做出判断该现场未拿到锁,后执行的A001因为拿到了锁,所以可以进行出价。
这两种方式实现应该都是属于乐观锁吧,上述实现可能暂时不适应什么秒杀之类的并发环境。总之具体问题还是得具体分析吧。
基于Redis的分布式锁两种实现方式的更多相关文章
- 基于redis的分布式锁二种应用场景
“分布式锁”是用来解决分布式应用中“并发冲突”的一种常用手段,实现方式一般有基于zookeeper及基于redis二种.具体到业务场景中,我们要考虑二种情况: 一.抢不到锁的请求,允许丢弃(即:忽略) ...
- 基于Redis的分布式锁真的安全吗?
说明: 我前段时间写了一篇用consul实现分布式锁,感觉理解的也不是很好,直到我看到了这2篇写分布式锁的讨论,真的是很佩服作者严谨的态度, 把这种分布式锁研究的这么透彻,作者这种技术态度真的值得我好 ...
- 基于redis的分布式锁(转)
基于redis的分布式锁 1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分 ...
- 基于redis的分布式锁(不适合用于生产环境)
基于redis的分布式锁 1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分 ...
- 基于 redis 的分布式锁实现 Distributed locks with Redis debug 排查错误
小结: 1. 锁的实现方式,按照应用的实现架构,可能会有以下几种类型: 如果处理程序是单进程多线程的,在 python下,就可以使用 threading 模块的 Lock 对象来限制对共享变量的同步访 ...
- 转载:基于Redis实现分布式锁
转载:基于Redis实现分布式锁 ,出处: http://blog.csdn.net/ugg/article/details/41894947 背景在很多互联网产品应用中,有些场景需要加锁处理,比如 ...
- redis系列:基于redis的分布式锁
一.介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分为两部分,一个是单机环境, ...
- 基于redis的分布式锁的分析与实践
前言:在分布式环境中,我们经常使用锁来进行并发控制,锁可分为乐观锁和悲观锁,基于数据库版本戳的实现是乐观锁,基于redis或zookeeper的实现可认为是悲观锁了.乐观锁和悲观锁最根本的区别在于 ...
- [Redis] 基于redis的分布式锁
前言分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁. 可靠性首先,为了确保 ...
随机推荐
- JDBC存储和读取二进制数据
以下JSP文件用common-fileupload组件实现文件上传,并将文件以二进制文件的形式存入数据库 <% if("POST".equalsIgnoreCase(requ ...
- Cocoa惯性思维调试一例
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 人总有惯性思维,在编程调试里也不例外.你总以为错误是显然的那一 ...
- Ext JS 6开发实例(三) :主界面设计
在上文中,已经将CMD创建的应用程序导入到项目里了,而且也看到默认的主界面了,今天的主要工作就是修改这个主界面,以符合项目的需要.除了设计主界面,还有一些其他的东西需要配置一下. 添加本地化包 打开a ...
- 发运模块中如何创建Debug 文件
版本11.5.9到12.x A. 针对发运事务处理或者快速发运产生Debug文件 注意:如果通过发运事务处理执行发放,请参考B部分,下面这部分销售订单发放是格外的设置和日志文件. 1. 每一 ...
- Struts2进阶(一)运行原理及搭建步骤
Struts2进阶(一)运行原理 Struts2框架 Struts2框架搭建步骤 致力于web服务,不可避免的涉及到编程实现部分功能.考虑使用到SSH框架中的Struts2.本篇文章只为深入理解Str ...
- volatile适用场合
volatile适用场合 要在多线程中安全的适用volatitle变量,必须同时满足: 1.对变量的写入操作不依赖其当前值 不满足:number++.count = count * 5等 ...
- JAVA之旅(十七)——StringBuffer的概述,存储,删除,获取,修改,反转,将缓存区的数据存储到数组中,StringBuilder
JAVA之旅(十七)--StringBuffer的概述,存储,删除,获取,修改,反转,将缓存区的数据存储到数组中,StringBuilder 讲完String,我们来聊聊他的小兄弟 一.StringB ...
- 一致性Hash算法介绍(分布式环境算法)
32的整数环(这个环被称作一致性Hash环),根据节点名称的Hash值(其分布范围同样为0~232)将节点放置在这个Hash 环上.然后根据KEY值计算得到其Hash值(其分布范围也同样为0~232 ...
- JAVA之旅(八)——多态的体现,前提,好处,应用,转型,instanceof,多态中成员变量的特点,多态的案例
JAVA之旅(八)--多态的体现,前提,好处,应用,转型,instanceof,多态中成员变量的特点,多态的案例 学习是不能停止的 一.多态 我们今天又要学习一个新的概念了,就是多态,它是面向对象的第 ...
- Cannot start service MSSQL$MICROSOFT##WID on computer
在做ADFS部署过程中配置ADFS服务时遇到如下问题 检查系统日志错误日志如下,很明显"NT SERVICE\MSSQL$MICROSOFT##WID"这个账户不在log on a ...