加入收藏 | 设为首页 | 会员中心 | 我要投稿 安阳站长网 (https://www.0372zz.cn/)- 高性能计算、分布式云、混合云存储、云计算、视频终端!
当前位置: 首页 > 站长学院 > MySql教程 > 正文

Redis实现分布式锁的正确姿势

发布时间:2019-01-31 05:06:50 所属栏目:MySql教程 来源:编辑之路
导读:一、前言 在我们日常工作中,除了Spring和Mybatis外,用到最多无外乎分布式缓存框架Redis。但是很多工作很多年的朋友对Redis还处于一个最基础的使用和认识。所以我就像把自己对分布式缓存的一些理解和应用整理一个系列,希望可以帮助到大家加深对Redis的理

Redis分布式锁实现的正确姿势的实现代码:

  1. public interface DistributedLock { 
  2.     /** 
  3.      * 获取锁 
  4.      * @author zhi.li 
  5.      * @return 锁标识 
  6.      */ 
  7.     String acquire(); 
  8.  
  9.     /** 
  10.      * 释放锁 
  11.      * @author zhi.li 
  12.      * @param indentifier 
  13.      * @return 
  14.      */ 
  15.     boolean release(String indentifier); 
  16. } 
  17.  
  18. /** 
  19.  * @author zhi.li 
  20.  * @Description 
  21.  * @created 2019/1/1 20:32 
  22.  */ 
  23. @Slf4j 
  24. public class RedisDistributedLock implements DistributedLock{ 
  25.  
  26.     private static final String LOCK_SUCCESS = "OK"; 
  27.     private static final Long RELEASE_SUCCESS = 1L; 
  28.     private static final String SET_IF_NOT_EXIST = "NX"; 
  29.     private static final String SET_WITH_EXPIRE_TIME = "PX"; 
  30.  
  31.     /** 
  32.      * redis 客户端 
  33.      */ 
  34.     private Jedis jedis; 
  35.  
  36.     /** 
  37.      * 分布式锁的键值 
  38.      */ 
  39.     private String lockKey; 
  40.  
  41.     /** 
  42.      * 锁的超时时间 10s 
  43.      */ 
  44.     int expireTime = 10 * 1000; 
  45.  
  46.     /** 
  47.      * 锁等待,防止线程饥饿 
  48.      */ 
  49.     int acquireTimeout  = 1 * 1000; 
  50.  
  51.     /** 
  52.      * 获取指定键值的锁 
  53.      * @param jedis jedis Redis客户端 
  54.      * @param lockKey 锁的键值 
  55.      */ 
  56.     public RedisDistributedLock(Jedis jedis, String lockKey) { 
  57.         this.jedis = jedis; 
  58.         this.lockKey = lockKey; 
  59.     } 
  60.  
  61.     /** 
  62.      * 获取指定键值的锁,同时设置获取锁超时时间 
  63.      * @param jedis jedis Redis客户端 
  64.      * @param lockKey 锁的键值 
  65.      * @param acquireTimeout 获取锁超时时间 
  66.      */ 
  67.     public RedisDistributedLock(Jedis jedis,String lockKey, int acquireTimeout) { 
  68.         this.jedis = jedis; 
  69.         this.lockKey = lockKey; 
  70.         this.acquireTimeout = acquireTimeout; 
  71.     } 
  72.  
  73.     /** 
  74.      * 获取指定键值的锁,同时设置获取锁超时时间和锁过期时间 
  75.      * @param jedis jedis Redis客户端 
  76.      * @param lockKey 锁的键值 
  77.      * @param acquireTimeout 获取锁超时时间 
  78.      * @param expireTime 锁失效时间 
  79.      */ 
  80.     public RedisDistributedLock(Jedis jedis, String lockKey, int acquireTimeout, int expireTime) { 
  81.         this.jedis = jedis; 
  82.         this.lockKey = lockKey; 
  83.         this.acquireTimeout = acquireTimeout; 
  84.         this.expireTime = expireTime; 
  85.     } 
  86.  
  87.     @Override 
  88.     public String acquire() { 
  89.         try { 
  90.             // 获取锁的超时时间,超过这个时间则放弃获取锁 
  91.             long end = System.currentTimeMillis() + acquireTimeout; 
  92.             // 随机生成一个value 
  93.             String requireToken = UUID.randomUUID().toString(); 
  94.             while (System.currentTimeMillis() < end) { 
  95.                 String result = jedis.set(lockKey, requireToken, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime); 
  96.                 if (LOCK_SUCCESS.equals(result)) { 
  97.                     return requireToken; 
  98.                 } 
  99.                 try { 
  100.                     Thread.sleep(100); 
  101.                 } catch (InterruptedException e) { 
  102.                     Thread.currentThread().interrupt(); 
  103.                 } 
  104.             } 
  105.         } catch (Exception e) { 
  106.             log.error("acquire lock due to error", e); 
  107.         } 
  108.  
  109.         return null; 
  110.     } 
  111.  
  112.     @Override 
  113.     public boolean release(String identify) { 
  114.   if(identify == null){ 
  115.             return false; 
  116.         } 
  117.  
  118.         String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; 
  119.         Object result = new Object(); 
  120.         try { 
  121.             result = jedis.eval(script, Collections.singletonList(lockKey), 
  122.                 Collections.singletonList(identify)); 
  123.         if (RELEASE_SUCCESS.equals(result)) { 
  124.             log.info("release lock success, requestToken:{}", identify); 
  125.             return true; 
  126.         }}catch (Exception e){ 
  127.             log.error("release lock due to error",e); 
  128.         }finally { 
  129.             if(jedis != null){ 
  130.                 jedis.close(); 
  131.             } 
  132.         } 
  133.  
  134.         log.info("release lock failed, requestToken:{}, result:{}", identify, result); 
  135.         return false; 
  136.     } 
  137. } 
下面就以秒杀库存数量为场景,测试下上面实现的分布式锁的效果。具体测试代码如下:
  1. public class RedisDistributedLockTest { 
  2.     static int n = 500; 
  3.     public static void secskill() { 
  4.         System.out.println(--n); 
  5.     } 
  6.  
  7.     public static void main(String[] args) { 
  8.         Runnable runnable = () -> { 
  9.             RedisDistributedLock lock = null; 
  10.             String unLockIdentify = null; 
  11.             try { 
  12.                 Jedis conn = new Jedis("127.0.0.1",6379); 
  13.                 lock = new RedisDistributedLock(conn, "test1"); 
  14.                 unLockIdentify = lock.acquire(); 
  15.                 System.out.println(Thread.currentThread().getName() + "正在运行"); 
  16.                 在此我向大家推荐一个架构学习交流圈:830478757 帮助突破瓶颈 提升思维能力 
  17.                 secskill(); 
  18.             } finally { 
  19.                 if (lock != null) { 
  20.                     lock.release(unLockIdentify); 
  21.                 } 
  22.             } 
  23.         }; 
  24.  
  25.         for (int i = 0; i < 10; i++) { 
  26.             Thread t = new Thread(runnable); 
  27.             t.start(); 
  28.         } 
  29.     } 
  30. } 

运行效果如下图所示。从图中可以看出,同一个资源在同一个时刻只能被一个线程获取,从而保证了库存数量N的递减是顺序的。

五、总结

(编辑:安阳站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读