实现

使用的是jedis来连接Redis。

实现思想

  • 获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。
  • 获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。
  • 释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放
  1. import redis.clients.jedis.Jedis;
  2. import redis.clients.jedis.JedisPool;
  3. import redis.clients.jedis.Transaction;
  4. import redis.clients.jedis.exceptions.JedisException;
  5.  
  6. import java.util.List;
  7. import java.util.UUID;
  8.  
  9. /**
  10. * Created by liuyang on 2017/4/20.
  11. */
  12. public class DistributedLock {
  13. private final JedisPool jedisPool;
  14.  
  15. public DistributedLock(JedisPool jedisPool) {
  16. this.jedisPool = jedisPool;
  17. }
  18.  
  19. /**
  20. * 加锁
  21. * @param locaName 锁的key
  22. * @param acquireTimeout 获取超时时间
  23. * @param timeout 锁的超时时间
  24. * @return 锁标识
  25. */
  26. public String lockWithTimeout(String locaName,
  27. long acquireTimeout, long timeout) {
  28. Jedis conn = null;
  29. String retIdentifier = null;
  30. try {
  31. // 获取连接
  32. conn = jedisPool.getResource();
  33. // 随机生成一个value
  34. String identifier = UUID.randomUUID().toString();
  35. // 锁名,即key值
  36. String lockKey = "lock:" + locaName;
  37. // 超时时间,上锁后超过此时间则自动释放锁
  38. int lockExpire = (int)(timeout / 1000);
  39.  
  40. // 获取锁的超时时间,超过这个时间则放弃获取锁
  41. long end = System.currentTimeMillis() + acquireTimeout;
  42. while (System.currentTimeMillis() < end) {
  43. if (conn.setnx(lockKey, identifier) == 1) {
  44. conn.expire(lockKey, lockExpire);
  45. // 返回value值,用于释放锁时间确认
  46. retIdentifier = identifier;
  47. return retIdentifier;
  48. }
  49. // 返回-1代表key没有设置超时时间,为key设置一个超时时间
  50. if (conn.ttl(lockKey) == -1) {
  51. conn.expire(lockKey, lockExpire);
  52. }
  53.  
  54. try {
  55. Thread.sleep(10);
  56. } catch (InterruptedException e) {
  57. Thread.currentThread().interrupt();
  58. }
  59. }
  60. } catch (JedisException e) {
  61. e.printStackTrace();
  62. } finally {
  63. if (conn != null) {
  64. conn.close();
  65. }
  66. }
  67. return retIdentifier;
  68. }
  69.  
  70. /**
  71. * 释放锁
  72. * @param lockName 锁的key
  73. * @param identifier 释放锁的标识
  74. * @return
  75. */
  76. public boolean releaseLock(String lockName, String identifier) {
  77. Jedis conn = null;
  78. String lockKey = "lock:" + lockName;
  79. boolean retFlag = false;
  80. try {
  81. conn = jedisPool.getResource();
  82. while (true) {
  83. // 监视lock,准备开始事务
  84. conn.watch(lockKey);
  85. // 通过前面返回的value值判断是不是该锁,若是该锁,则删除,释放锁
  86. if (identifier.equals(conn.get(lockKey))) {
  87. Transaction transaction = conn.multi();
  88. transaction.del(lockKey);
  89. List<Object> results = transaction.exec();
  90. if (results == null) {
  91. continue;
  92. }
  93. retFlag = true;
  94. }
  95. conn.unwatch();
  96. break;
  97. }
  98. } catch (JedisException e) {
  99. e.printStackTrace();
  100. } finally {
  101. if (conn != null) {
  102. conn.close();
  103. }
  104. }
  105. return retFlag;
  106. }
  107. }

  

测试

下面就用一个简单的例子测试刚才实现的分布式锁。
例子中使用50个线程模拟秒杀一个商品,使用--运算符来实现商品减少,从结果有序性就可以看出是否为加锁状态。

模拟秒杀服务,在其中配置了jedis线程池,在初始化的时候传给分布式锁,供其使用。

  1. import redis.clients.jedis.JedisPool;
  2. import redis.clients.jedis.JedisPoolConfig;
  3.  
  4. /**
  5. * Created by liuyang on 2017/4/20.
  6. */
  7. public class Service {
  8. private static JedisPool pool = null;
  9.  
  10. static {
  11. JedisPoolConfig config = new JedisPoolConfig();
  12. // 设置最大连接数
  13. config.setMaxTotal(200);
  14. // 设置最大空闲数
  15. config.setMaxIdle(8);
  16. // 设置最大等待时间
  17. config.setMaxWaitMillis(1000 * 100);
  18. // 在borrow一个jedis实例时,是否需要验证,若为true,则所有jedis实例均是可用的
  19. config.setTestOnBorrow(true);
  20. pool = new JedisPool(config, "127.0.0.1", 6379, 3000);
  21. }
  22.  
  23. DistributedLock lock = new DistributedLock(pool);
  24.  
  25. int n = 500;
  26.  
  27. public void seckill() {
  28. // 返回锁的value值,供释放锁时候进行判断
  29. String indentifier = lock.lockWithTimeout("resource", 5000, 1000);
  30. System.out.println(Thread.currentThread().getName() + "获得了锁");
  31. System.out.println(--n);
  32. lock.releaseLock("resource", indentifier);
  33. }
  34. }

  // 模拟线程进行秒杀服务

  1. public class ThreadA extends Thread {
  2. private Service service;
  3.  
  4. public ThreadA(Service service) {
  5. this.service = service;
  6. }
  7.  
  8. @Override
  9. public void run() {
  10. service.seckill();
  11. }
  12. }
  13.  
  14. public class Test {
  15. public static void main(String[] args) {
  16. Service service = new Service();
  17. for (int i = 0; i < 50; i++) {
  18. ThreadA threadA = new ThreadA(service);
  19. threadA.start();
  20. }
  21. }
  22. }

  

在分布式环境中,对资源进行上锁有时候是很重要的,比如抢购某一资源,这时候使用分布式锁就可以很好地控制资源。
当然,在具体使用中,还需要考虑很多因素,比如超时时间的选取,获取锁时间的选取对并发量都有很大的影响,上述实现的分布式锁也只是一种简单的实现,主要是一种思想。

下一次我会使用zookeeper实现分布式锁,使用zookeeper的可靠性是要大于使用redis实现的分布式锁的,但是相比而言,redis的性能更好。

http://www.cnblogs.com/liuyang0/p/6744076.html

基于Redis实现——分布式锁与实现的更多相关文章

  1. 基于redis 实现分布式锁的方案

    在电商项目中,经常有秒杀这样的活动促销,在并发访问下,很容易出现上述问题.如果在库存操作上,加锁就可以避免库存卖超的问题.分布式锁使分布式系统之间同步访问共享资源的一种方式 基于redis实现分布式锁 ...

  2. 基于redis的分布式锁

    <?php /** * 基于redis的分布式锁 * * 参考开源代码: * http://nleach.com/post/31299575840/redis-mutex-in-php * * ...

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

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

  4. 基于 Redis 的分布式锁

    前言 分布式锁在分布式应用中应用广泛,想要搞懂一个新事物首先得了解它的由来,这样才能更加的理解甚至可以举一反三. 首先谈到分布式锁自然也就联想到分布式应用. 在我们将应用拆分为分布式应用之前的单机系统 ...

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

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

  6. 基于redis的分布式锁实现

    1.分布式锁介绍 在计算机系统中,锁作为一种控制并发的机制无处不在. 单机环境下,操作系统能够在进程或线程之间通过本地的锁来控制并发程序的行为.而在如今的大型复杂系统中,通常采用的是分布式架构提供服务 ...

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

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

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

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

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

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

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

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

随机推荐

  1. 前端PHP入门-008-自定义函数

    大家已经是有编程经验的WEB前端,那么对于函数已经很熟悉了,PHP当中定义函数跟你们学习的JavaScript一样 想想有哪些函数类型? 我们在实际开发过程当中需要有很多功能都需要反复使用到,而这些反 ...

  2. Linux 下 JDK + Eclipse + PyDev 安装与配置

    一:JDK / JRE 环境 Eclipse 是运行于Java虚拟机中的,所以必须先安装Java环境才能进行开发测试.JRE(Java Runtime Environment)是运行环境,JDK(Ja ...

  3. WPF技术点

    常用Path路径 正三角形(左):<Path Data="M40,0 L0,30 40,60 z" Stretch="Uniform"/> 正三角形 ...

  4. DevExpress 常用命令包括导出-打印-打印预览等

    3.表格打印也是最常见的,打印代码如下: PrintingSystem ps = null; DevExpress.XtraPrinting.PrintableComponentLink link = ...

  5. angular package.json中start build

    "start": "ng serve --host 0.0.0.0 --port 4200 --proxy-config proxy.conf.json", & ...

  6. 【CodeForces】698 C. LRU

    [题目]C. LRU [题意]给定空间为k的背包和n个物品,每次每个物品有pi的概率加入(Σpi=1),加入时若发现背包中已有该物品则不改变,若背包满k个物品后再加入新物品则弹出最早加入的物品,求加入 ...

  7. c++ virtual总结

    virtual-关键字用于修饰成员函数时,有以下特性 1.用于修饰的基类的成员函数,被修饰的基类成员函数-其派生类的同名成员函数也默认带有virtual 关键字2.当virtual 用于修饰析构函数( ...

  8. 【CF343D】 Water Tree(树链剖分)

    题目链接 树剖傻逼题,练练手好久没写树剖了. 查询忘记\(pushdown\)抓了好久虫.. 全文手写,一遍过... #include <cstdio> const int MAXN = ...

  9. # 2018高考&自主招生 - 游记

    准备了一整个学期的高考和自主招生终于结束了....从育英出来, 以一个失败者的身份来写游记, 权当为明年的决战提供经验与总结. Day -1, June 5th 下午同学收拾考场, 自己在那里看书.. ...

  10. vue.js devtools-------调试vue.js的开发者插件

    vue.js devtools插件: 作用: 以往我们在进行测试代码的时候,直接在console进行查看,其实这个插件雷同于控制台,只不过在vue里面,将需要查看的数据存放在一个变量里面了~ 效果图: ...