Java实现基于Redis的分布式锁

我这里不实现JDK的java.util.concurrent.locks.Lock接口,而是自定义一个,因为JDK的有个newCondition()方法我这里暂时没实现。这个Lock提供了5个lock方法的变体,可以自行选择使用哪一个来获取锁,我的想法是

最好用带超时返回的那几个方法,因为不这样的话,假如redis挂了,线程永远都在那死循环了(关于这里,应该还可以进一步优化,如果redis挂了,Jedis的操作肯定会抛异常之类的,可以定义个机制让redis挂了的时候通知使用这个lock的用户,或者说是线程)

Java代码 下载

  1. package cc.lixiaohui.lock;
  2. import java.util.concurrent.TimeUnit;
  3. public interface Lock {
  4. /**
  5. * 阻塞性的获取锁, 不响应中断
  6. */
  7. void lock();
  8. /**
  9. * 阻塞性的获取锁, 响应中断
  10. *
  11. * @throws InterruptedException
  12. */
  13. void lockInterruptibly() throws InterruptedException;
  14. /**
  15. * 尝试获取锁, 获取不到立即返回, 不阻塞
  16. */
  17. boolean tryLock();
  18. /**
  19. * 超时自动返回的阻塞性的获取锁, 不响应中断
  20. *
  21. * @param time
  22. * @param unit
  23. * @return {@code true} 若成功获取到锁, {@code false} 若在指定时间内未获取到锁
  24. *
  25. */
  26. boolean tryLock(long time, TimeUnit unit);
  27. /**
  28. * 超时自动返回的阻塞性的获取锁, 响应中断
  29. *
  30. * @param time
  31. * @param unit
  32. * @return {@code true} 若成功获取到锁, {@code false} 若在指定时间内未获取到锁
  33. * @throws InterruptedException 在尝试获取锁的当前线程被中断
  34. */
  35. boolean tryLockInterruptibly(long time, TimeUnit unit) throws InterruptedException;
  36. /**
  37. * 释放锁
  38. */
  39. void unlock();
  40. }

看其抽象实现:

Java代码 下载

  1. package cc.lixiaohui.lock;
  2. import java.util.concurrent.TimeUnit;
  3. /**
  4. * 锁的骨架实现, 真正的获取锁的步骤由子类去实现.
  5. *
  6. * @author lixiaohui
  7. *
  8. */
  9. public abstract class AbstractLock implements Lock {
  10. /**
  11. * <pre>
  12. * 这里需不需要保证可见性值得讨论, 因为是分布式的锁,
  13. * 1.同一个jvm的多个线程使用不同的锁对象其实也是可以的, 这种情况下不需要保证可见性
  14. * 2.同一个jvm的多个线程使用同一个锁对象, 那可见性就必须要保证了.
  15. * </pre>
  16. */
  17. protected volatile boolean locked;
  18. /**
  19. * 当前jvm内持有该锁的线程(if have one)
  20. */
  21. private Thread exclusiveOwnerThread;
  22. public void lock() {
  23. try {
  24. lock(false, 0, null, false);
  25. } catch (InterruptedException e) {
  26. // TODO ignore
  27. }
  28. }
  29. public void lockInterruptibly() throws InterruptedException {
  30. lock(false, 0, null, true);
  31. }
  32. public boolean tryLock(long time, TimeUnit unit) {
  33. try {
  34. return lock(true, time, unit, false);
  35. } catch (InterruptedException e) {
  36. // TODO ignore
  37. }
  38. return false;
  39. }
  40. public boolean tryLockInterruptibly(long time, TimeUnit unit) throws InterruptedException {
  41. return lock(true, time, unit, true);
  42. }
  43. public void unlock() {
  44. // TODO 检查当前线程是否持有锁
  45. if (Thread.currentThread() != getExclusiveOwnerThread()) {
  46. throw new IllegalMonitorStateException("current thread does not hold the lock");
  47. }
  48. unlock0();
  49. setExclusiveOwnerThread(null);
  50. }
  51. protected void setExclusiveOwnerThread(Thread thread) {
  52. exclusiveOwnerThread = thread;
  53. }
  54. protected final Thread getExclusiveOwnerThread() {
  55. return exclusiveOwnerThread;
  56. }
  57. protected abstract void unlock0();
  58. /**
  59. * 阻塞式获取锁的实现
  60. *
  61. * @param useTimeout
  62. * @param time
  63. * @param unit
  64. * @param interrupt 是否响应中断
  65. * @return
  66. * @throws InterruptedException
  67. */
  68. protected abstract boolean lock(boolean useTimeout, long time, TimeUnit unit, boolean interrupt) throws InterruptedException;
  69. }

基于Redis的最终实现,关键的获取锁,释放锁的代码在这个类的lock方法和unlock0方法里,大家可以只看这两个方法然后完全自己写一个:

Java代码 下载

  1. package cc.lixiaohui.lock;
  2. import java.util.concurrent.TimeUnit;
  3. import redis.clients.jedis.Jedis;
  4. /**
  5. * <pre>
  6. * 基于Redis的SETNX操作实现的分布式锁
  7. *
  8. * 获取锁时最好用lock(long time, TimeUnit unit), 以免网路问题而导致线程一直阻塞
  9. *
  10. * <a href="http://redis.io/commands/setnx">SETNC操作参考资料</a>
  11. * </pre>
  12. *
  13. * @author lixiaohui
  14. *
  15. */
  16. public class RedisBasedDistributedLock extends AbstractLock {
  17. private Jedis jedis;
  18. // 锁的名字
  19. protected String lockKey;
  20. // 锁的有效时长(毫秒)
  21. protected long lockExpires;
  22. public RedisBasedDistributedLock(Jedis jedis, String lockKey, long lockExpires) {
  23. this.jedis = jedis;
  24. this.lockKey = lockKey;
  25. this.lockExpires = lockExpires;
  26. }
  27. // 阻塞式获取锁的实现
  28. protected boolean lock(boolean useTimeout, long time, TimeUnit unit, boolean interrupt) throws InterruptedException{
  29. if (interrupt) {
  30. checkInterruption();
  31. }
  32. long start = System.currentTimeMillis();
  33. long timeout = unit.toMillis(time); // if !useTimeout, then it‘s useless
  34. while (useTimeout ? isTimeout(start, timeout) : true) {
  35. if (interrupt) {
  36. checkInterruption();
  37. }
  38. long lockExpireTime = System.currentTimeMillis() + lockExpires + 1;//锁超时时间
  39. String stringOfLockExpireTime = String.valueOf(lockExpireTime);
  40. if (jedis.setnx(lockKey, stringOfLockExpireTime) == 1) { // 获取到锁
  41. // TODO 成功获取到锁, 设置相关标识
  42. locked = true;
  43. setExclusiveOwnerThread(Thread.currentThread());
  44. return true;
  45. }
  46. String value = jedis.get(lockKey);
  47. if (value != null && isTimeExpired(value)) { // lock is expired
  48. // 假设多个线程(非单jvm)同时走到这里
  49. String oldValue = jedis.getSet(lockKey, stringOfLockExpireTime); // getset is atomic
  50. // 但是走到这里时每个线程拿到的oldValue肯定不可能一样(因为getset是原子性的)
  51. // 加入拿到的oldValue依然是expired的,那么就说明拿到锁了
  52. if (oldValue != null && isTimeExpired(oldValue)) {
  53. // TODO 成功获取到锁, 设置相关标识
  54. locked = true;
  55. setExclusiveOwnerThread(Thread.currentThread());
  56. return true;
  57. }
  58. } else {
  59. // TODO lock is not expired, enter next loop retrying
  60. }
  61. }
  62. return false;
  63. }
  64. public boolean tryLock() {
  65. long lockExpireTime = System.currentTimeMillis() + lockExpires + 1;//锁超时时间
  66. String stringOfLockExpireTime = String.valueOf(lockExpireTime);
  67. if (jedis.setnx(lockKey, stringOfLockExpireTime) == 1) { // 获取到锁
  68. // TODO 成功获取到锁, 设置相关标识
  69. locked = true;
  70. setExclusiveOwnerThread(Thread.currentThread());
  71. return true;
  72. }
  73. String value = jedis.get(lockKey);
  74. if (value != null && isTimeExpired(value)) { // lock is expired
  75. // 假设多个线程(非单jvm)同时走到这里
  76. String oldValue = jedis.getSet(lockKey, stringOfLockExpireTime); // getset is atomic
  77. // 但是走到这里时每个线程拿到的oldValue肯定不可能一样(因为getset是原子性的)
  78. // 假如拿到的oldValue依然是expired的,那么就说明拿到锁了
  79. if (oldValue != null && isTimeExpired(oldValue)) {
  80. // TODO 成功获取到锁, 设置相关标识
  81. locked = true;
  82. setExclusiveOwnerThread(Thread.currentThread());
  83. return true;
  84. }
  85. } else {
  86. // TODO lock is not expired, enter next loop retrying
  87. }
  88. return false;
  89. }
  90. /**
  91. * Queries if this lock is held by any thread.
  92. *
  93. * @return {@code true} if any thread holds this lock and
  94. *         {@code false} otherwise
  95. */
  96. public boolean isLocked() {
  97. if (locked) {
  98. return true;
  99. } else {
  100. String value = jedis.get(lockKey);
  101. // TODO 这里其实是有问题的, 想:当get方法返回value后, 假设这个value已经是过期的了,
  102. // 而就在这瞬间, 另一个节点set了value, 这时锁是被别的线程(节点持有), 而接下来的判断
  103. // 是检测不出这种情况的.不过这个问题应该不会导致其它的问题出现, 因为这个方法的目的本来就
  104. // 不是同步控制, 它只是一种锁状态的报告.
  105. return !isTimeExpired(value);
  106. }
  107. }
  108. @Override
  109. protected void unlock0() {
  110. // TODO 判断锁是否过期
  111. String value = jedis.get(lockKey);
  112. if (!isTimeExpired(value)) {
  113. doUnlock();
  114. }
  115. }
  116. private void checkInterruption() throws InterruptedException {
  117. if(Thread.currentThread().isInterrupted()) {
  118. throw new InterruptedException();
  119. }
  120. }
  121. private boolean isTimeExpired(String value) {
  122. return Long.parseLong(value) < System.currentTimeMillis();
  123. }
  124. private boolean isTimeout(long start, long timeout) {
  125. return start + timeout > System.currentTimeMillis();
  126. }
  127. private void doUnlock() {
  128. jedis.del(lockKey);
  129. }
  130. }

如果将来还换一种实现方式(比如zookeeper之类的),到时直接继承AbstractLock并实现lock(boolean useTimeout, long time, TimeUnit unit, boolean interrupt), unlock0()方法即可(所谓抽象嘛)

时间: 2024-08-28 08:31:02

Java实现基于Redis的分布式锁的相关文章

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

基于redis的分布式锁 1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分为两部分,一个是单机环境,另一个是集群环境下的Redis锁实现.在介绍分布式锁的实现之前,先来了解下分布式锁的一些信息. 2 分布式锁 2.1 什么是分布式锁? 分布式锁是控制分布式系统或不同系统之间共同访问共享资源的一种锁实现,如果不同的系统或同一个系统的不同主机之间共享了某个资源时,往往需要互斥

基于redis的分布式锁实现

关于分布式锁 很久之前有讲过并发编程中的锁并发编程的锁机制:synchronized和lock.在单进程的系统中,当存在多个线程可以同时改变某个变量时,就需要对变量或代码块做同步,使其在修改这种变量时能够线性执行消除并发修改变量.而同步的本质是通过锁来实现的.为了实现多个线程在一个时刻同一个代码块只能有一个线程可执行,那么需要在某个地方做个标记,这个标记必须每个线程都能看到,当标记不存在时可以设置该标记,其余后续线程发现已经有标记了则等待拥有标记的线程结束同步代码块取消标记后再去尝试设置标记.

[Redis] 基于redis的分布式锁

前言分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁. 可靠性首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件: 互斥性.在任意时刻,只有一个客户端能持有锁.不会发生死锁.即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁.具有容错性.只要大部分的Redis节点正常运行,客户端就可以加锁和解锁.解铃还须系铃人.加锁和解锁必须

基于Redis的分布式锁到底安全吗(上)?

网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词"Redis 分布式锁"随便到哪个搜索引擎上去搜索一下就知道了.这些文章的思路大体相近,给出的实现算法也看似合乎逻辑,但当我们着手去实现它们的时候,却发现如果你越是仔细推敲,疑虑也就越来越多. 实际上,大概在一年以前,关于Redis分布式锁的安全性问题,在分布式系统专家Martin Kleppmann和Redis的作者antirez之间就发生过一场争论.由于对这个问题一直以来比较关注,所以我前些日子仔细阅读了与这场争

基于redis的分布式锁

<?php /** * 基于redis的分布式锁 * * 参考开源代码: * http://nleach.com/post/31299575840/redis-mutex-in-php * * https://gist.github.com/nickyleach/3694555 */ pc_base::load_sys_class('cache_redis', '', 0); class dist_key_redis { //锁的超时时间 const TIMEOUT = 20; const SL

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

转载:基于Redis实现分布式锁  ,出处: http://blog.csdn.net/ugg/article/details/41894947 背景在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等.大部分的解决方案是基于DB实现的,Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系.其次Redis提供一些命令SETNX,GETSET,可以方便实现分布式锁机制. Redis命令介绍使用Redis实现分

python基于redis实现分布式锁

阅读目录 什么事分布式锁 基于redis实现分布式锁 一.什么是分布式锁 我们在开发应用的时候,如果需要对某一个共享变量进行多线程同步访问的时候,可以使用我们学到的锁进行处理,并且可以完美的运行,毫无Bug! 注意这是单机应用,后来业务发展,需要做集群,一个应用需要部署到几台机器上然后做负载均衡,大致如下图: 上图可以看到,变量A存在三个服务器内存中(这个变量A主要体现是在一个类中的一个成员变量,是一个有状态的对象),如果不加任何控制的话,变量A同时都会在分配一块内存,三个请求发过来同时对这个变

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

转:https://my.oschina.net/wnjustdoit/blog/1606215 前言:在分布式环境中,我们经常使用锁来进行并发控制,锁可分为乐观锁和悲观锁,基于数据库版本戳的实现是乐观锁,基于redis或zookeeper的实现可认为是悲观锁了.乐观锁和悲观锁最根本的区别在于线程之间是否相互阻塞. 那么,本文主要来讨论基于redis的分布式锁算法问题. 从2.6.12版本开始,redis为SET命令增加了一系列选项(SET key value [EX seconds] [PX

基于Redis的分布式锁和Redlock算法

1 前言 前面写了4篇Redis底层实现和工程架构相关文章,感兴趣的读者可以回顾一下: Redis面试热点之底层实现篇-1 Redis面试热点之底层实现篇-2 Redis面试热点之工程架构篇-1 Redis面试热点之工程架构篇-2 今天开始来和大家一起学习一下Redis实际应用篇,会写几个Redis的常见应用. 在我看来Redis最为典型的应用就是作为分布式缓存系统,其他的一些应用本质上并不是杀手锏功能,是基于Redis支持的数据类型和分布式架构来实现的,属于小而美的应用. 结合笔者的日常工作,