最近有一个竞拍的项目会用到分布式锁,网上查到的结果是有三种途径可以实现。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的分布式锁两种实现方式的更多相关文章

  1. 基于redis的分布式锁二种应用场景

    “分布式锁”是用来解决分布式应用中“并发冲突”的一种常用手段,实现方式一般有基于zookeeper及基于redis二种.具体到业务场景中,我们要考虑二种情况: 一.抢不到锁的请求,允许丢弃(即:忽略) ...

  2. 基于Redis的分布式锁真的安全吗?

    说明: 我前段时间写了一篇用consul实现分布式锁,感觉理解的也不是很好,直到我看到了这2篇写分布式锁的讨论,真的是很佩服作者严谨的态度, 把这种分布式锁研究的这么透彻,作者这种技术态度真的值得我好 ...

  3. 基于redis的分布式锁(转)

    基于redis的分布式锁 1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分 ...

  4. 基于redis的分布式锁(不适合用于生产环境)

    基于redis的分布式锁 1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分 ...

  5. 基于 redis 的分布式锁实现 Distributed locks with Redis debug 排查错误

    小结: 1. 锁的实现方式,按照应用的实现架构,可能会有以下几种类型: 如果处理程序是单进程多线程的,在 python下,就可以使用 threading 模块的 Lock 对象来限制对共享变量的同步访 ...

  6. 转载:基于Redis实现分布式锁

    转载:基于Redis实现分布式锁  ,出处: http://blog.csdn.net/ugg/article/details/41894947 背景在很多互联网产品应用中,有些场景需要加锁处理,比如 ...

  7. redis系列:基于redis的分布式锁

    一.介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分为两部分,一个是单机环境, ...

  8. 基于redis的分布式锁的分析与实践

    ​ 前言:在分布式环境中,我们经常使用锁来进行并发控制,锁可分为乐观锁和悲观锁,基于数据库版本戳的实现是乐观锁,基于redis或zookeeper的实现可认为是悲观锁了.乐观锁和悲观锁最根本的区别在于 ...

  9. [Redis] 基于redis的分布式锁

    前言分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁. 可靠性首先,为了确保 ...

随机推荐

  1. [ExtJS5学习笔记]第六节 Extjs的类系统Class System命名规则及定义和调试

    本文地址: http://blog.csdn.net/sushengmiyan/article/details/38479079 本文作者:sushengmiyan ----------------- ...

  2. Oracle使用游标为所有用户表添加主键语句

    应用场合:数据表新增自增一主键能加快数据表的访问速度,而且是整形的索引速度最快.本程序适合在导入Oracle数据库时删除不存在主键的情况下运行. 代码说明:所有的表主键字段名都设置为ID,如果已存在I ...

  3. oracle对大对象类型操作:blob,clob,nclob

     1.基本介绍 Oracle和plsql都支持lob(large object) 类型,用来存储大数量数据,如图像文件,声音文件等.Oracle 9i realse2支持存储最大为4g的数据,or ...

  4. Swift基础之显示波纹样式

    最近项目用到了蓝牙连接,搜索设备的内容,其中需要搜索过程中出现波纹的动画效果,在这里将项目中OC语言编写的这种动画效果,转换成Swift编写,下面简单介绍说明一下代码. 这里用到了两种方法实现波纹效果 ...

  5. 学习笔记3-开发与运行(卸载)第一个ANDROID应用

    新建Android项目 1.      配置好Android坏境以后,新建项目选择Android Project. 2.      选择针对哪个平台开发的应用(Android2/Android4等) ...

  6. Deploying Customizations in Oracle E-Business Suite Release 12.2

    DeployingCustomizations in Oracle E-Business Suite Release 12.2 This documentdescribes how to deploy ...

  7. python上下文管理器ContextLib及with语句

    http://blog.csdn.net/pipisorry/article/details/50444736 with语句 with语句是从 Python 2.5 开始引入的一种与异常处理相关的功能 ...

  8. eclipse集成html5插件

    1. 在线安装:Help->Install New Software...->Add...-> Name: "Aptana", Location: http:// ...

  9. WIP完工入库及完工退回的几个重要问题

    1.必须向CST_COMP_SNAP_INTERFACE表中插入此工单所有工序的数据(也就是说同样的工单插入多条,只是工序号不一样) 标准文档: Note: If there are multiple ...

  10. android的Drawable详解

    Drawable简介 Drawable有很多种,用来表示一种图像的概念,但他们又不完全是图像,他们是用过颜色构建出来的各种图像的表现形式.Drawable一般都是通过xml来定义的 ,当然我们也可以通 ...