加入收藏 | 设为首页 | 会员中心 | 我要投稿 源码网 (https://www.900php.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > MySql教程 > 正文

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

发布时间:2019-02-03 11:40:26 所属栏目:MySql教程 来源:编辑之路
导读:副标题#e# 一、前言 在我们日常工作中,除了Spring和Mybatis外,用到最多无外乎分布式缓存框架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.  * @author zhi.li 
  19.  * @Description 
  20.  * @created 2019/1/1 20:32 
  21.  */ 
  22. @Slf4j 
  23. public class RedisDistributedLock implements DistributedLock{ 
  24.  
  25.     private static final String LOCK_SUCCESS = "OK"; 
  26.     private static final Long RELEASE_SUCCESS = 1L; 
  27.     private static final String SET_IF_NOT_EXIST = "NX"; 
  28.     private static final String SET_WITH_EXPIRE_TIME = "PX"; 
  29.  
  30.     /** 
  31.      * redis 客户端 
  32.      */ 
  33.     private Jedis jedis; 
  34.  
  35.     /** 
  36.      * 分布式锁的键值 
  37.      */ 
  38.     private String lockKey; 
  39.  
  40.     /** 
  41.      * 锁的超时时间 10s 
  42.      */ 
  43.     int expireTime = 10 * 1000; 
  44.  
  45.     /** 
  46.      * 锁等待,防止线程饥饿 
  47.      */ 
  48.     int acquireTimeout  = 1 * 1000; 
  49.  
  50.     /** 
  51.      * 获取指定键值的锁 
  52.      * @param jedis jedis Redis客户端 
  53.      * @param lockKey 锁的键值 
  54.      */ 
  55.     public RedisDistributedLock(Jedis jedis, String lockKey) { 
  56.         this.jedis = jedis; 
  57.         this.lockKey = lockKey; 
  58.     } 
  59.  
  60.     /** 
  61.      * 获取指定键值的锁,同时设置获取锁超时时间 
  62.      * @param jedis jedis Redis客户端 
  63.      * @param lockKey 锁的键值 
  64.      * @param acquireTimeout 获取锁超时时间 
  65.      */ 
  66.     public RedisDistributedLock(Jedis jedis,String lockKey, int acquireTimeout) { 
  67.         this.jedis = jedis; 
  68.         this.lockKey = lockKey; 
  69.         this.acquireTimeout = acquireTimeout; 
  70.     } 
  71.  
  72.     /** 
  73.      * 获取指定键值的锁,同时设置获取锁超时时间和锁过期时间 
  74.      * @param jedis jedis Redis客户端 
  75.      * @param lockKey 锁的键值 
  76.      * @param acquireTimeout 获取锁超时时间 
  77.      * @param expireTime 锁失效时间 
  78.      */ 
  79.     public RedisDistributedLock(Jedis jedis, String lockKey, int acquireTimeout, int expireTime) { 
  80.         this.jedis = jedis; 
  81.         this.lockKey = lockKey; 
  82.         this.acquireTimeout = acquireTimeout; 
  83.         this.expireTime = expireTime; 
  84.     } 
  85.  
  86.     @Override 
  87.     public String acquire() { 
  88.         try { 
  89.             // 获取锁的超时时间,超过这个时间则放弃获取锁 
  90.             long end = System.currentTimeMillis() + acquireTimeout; 
  91.             // 随机生成一个value 
  92.             String requireToken = UUID.randomUUID().toString(); 
  93.             while (System.currentTimeMillis() < end) { 
  94.                 String result = jedis.set(lockKey, requireToken, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime); 
  95.                 if (LOCK_SUCCESS.equals(result)) { 
  96.                     return requireToken; 
  97.                 } 
  98.                 try { 
  99.                     Thread.sleep(100); 
  100.                 } catch (InterruptedException e) { 
  101.                     Thread.currentThread().interrupt(); 
  102.                 } 
  103.             } 
  104.         } catch (Exception e) { 
  105.             log.error("acquire lock due to error", e); 
  106.         } 
  107.  
  108.         return null; 
  109.     } 
  110.  
  111.     @Override 
  112.     public boolean release(String identify) { 
  113.   if(identify == null){ 
  114.             return false; 
  115.         } 
  116.  
  117.         String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; 
  118.         Object result = new Object(); 
  119.         try { 
  120.             result = jedis.eval(script, Collections.singletonList(lockKey), 
  121.                 Collections.singletonList(identify)); 
  122.         if (RELEASE_SUCCESS.equals(result)) { 
  123.             log.info("release lock success, requestToken:{}", identify); 
  124.             return true; 
  125.         }}catch (Exception e){ 
  126.             log.error("release lock due to error",e); 
  127.         }finally { 
  128.             if(jedis != null){ 
  129.                 jedis.close(); 
  130.             } 
  131.         } 
  132.  
  133.         log.info("release lock failed, requestToken:{}, result:{}", identify, result); 
  134.         return false; 
  135.     } 
下面就以秒杀库存数量为场景,测试下上面实现的分布式锁的效果。具体测试代码如下:
  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.     } 

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

五、总结

(编辑:源码网)

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

热点阅读