转载请标明出处。

在分布式系统中,常常会出现须要竞争同一资源的情况,本代码基于redis3.0.1+jedis2.7.1实现了分布式锁。

redis集群的搭建,请见我的另外一篇文章:<>《redis3.0.1集群环境搭建》

可用于比如秒杀系统中的商品库存的管理。付完整代码及測试用例。

  1. package com.gaojiasoft.gaojiaRedis;
  2.  
  3. import java.util.UUID;
  4. import java.util.concurrent.LinkedBlockingQueue;
  5. import java.util.concurrent.ThreadPoolExecutor;
  6. import java.util.concurrent.TimeUnit;
  7.  
  8. import javax.annotation.PostConstruct;
  9.  
  10. import org.apache.commons.lang3.RandomUtils;
  11. import org.apache.commons.lang3.concurrent.BasicThreadFactory;
  12. import org.slf4j.Logger;
  13. import org.slf4j.LoggerFactory;
  14. import org.springframework.beans.factory.annotation.Autowired;
  15.  
  16. import redis.clients.jedis.JedisCluster;
  17. import redis.clients.jedis.exceptions.JedisConnectionException;
  18.  
  19. /**
  20. * 分布式锁管理器,支持对单个资源加锁解锁。或给一批资源的批量加锁及解锁
  21. * @ClassName: DistributedLockManger
  22. * @Description:
  23. * @author 何志雄 001
  24. * @company 南京高嘉软件技术有限公司
  25. * @date 2015年6月3日 下午5:44:06
  26. */
  27. public class DistributedLockManger
  28. {
  29.  
  30. private static final Logger LOGGER = LoggerFactory.getLogger(DistributedLockManger.class);
  31.  
  32. private static final int DEFAULT_SINGLE_EXPIRE_TIME = 3;
  33.  
  34. // private static final int DEFAULT_BATCH_EXPIRE_TIME = 6;
  35.  
  36. //static的变量无法注解
  37. @Autowired
  38. JedisCluster jc;
  39.  
  40. private static DistributedLockManger lockManger;
  41.  
  42. public DistributedLockManger()
  43. {
  44. }
  45.  
  46. @PostConstruct
  47. public void init()
  48. {
  49. lockManger = this;
  50. lockManger.jc = this.jc;
  51. }
  52.  
  53. /**
  54. * 获取锁 假设锁可用 马上返回true。 否则马上返回false,作为非堵塞式锁使用
  55. * @param key
  56. * @param value
  57. * @return
  58. */
  59. public static boolean tryLock(String key , String value)
  60. {
  61. try
  62. {
  63. return tryLock(key, value, 0L, null);
  64. }
  65. catch (InterruptedException e)
  66. {
  67. e.printStackTrace();
  68. }
  69. return false;
  70. }
  71.  
  72. /**
  73. * 锁在给定的等待时间内空暇,则获取锁成功 返回true, 否则返回false。作为堵塞式锁使用
  74. * @param key 锁键
  75. * @param value 被谁锁定
  76. * @param timeout 尝试获取锁时长。建议传递500,结合实践单位。则可表示500毫秒
  77. * @param unit。建议传递TimeUnit.MILLISECONDS
  78. * @return
  79. * @throws InterruptedException
  80. */
  81. public static boolean tryLock(String key , String value , long timeout , TimeUnit unit) throws InterruptedException
  82. {
  83. //纳秒
  84. long begin = System.nanoTime();
  85. do
  86. {
  87. //LOGGER.debug("{}尝试获得{}的锁.", value, key);
  88. Long i = lockManger.jc.setnx(key, value);
  89. if (i == 1)
  90. {
  91. lockManger.jc.expire(key, DEFAULT_SINGLE_EXPIRE_TIME);
  92. LOGGER.debug("{}成功获取{}的锁,设置锁过期时间为{}秒 ", value, key, DEFAULT_SINGLE_EXPIRE_TIME);
  93. return true;
  94. }
  95. else
  96. {
  97. // 存在锁 ,但可能获取不到,原因是获取的一刹那间
  98. // String desc = lockManger.jc.get(key);
  99. // LOGGER.error("{}正被{}锁定.", key, desc);
  100. }
  101. if (timeout == 0)
  102. {
  103. break;
  104. }
  105. //在其睡眠的期间,锁可能被解,也可能又被他人占用,但会尝试继续获取锁直到指定的时间
  106. Thread.sleep(100);
  107. }
  108. while ((System.nanoTime() - begin) < unit.toNanos(timeout));
  109. //因超时没有获得锁
  110. return false;
  111. }
  112.  
  113. /**
  114. * 释放单个锁
  115. * @param key 锁键
  116. * @param value 被谁释放
  117. */
  118. public static void unLock(String key , String value)
  119. {
  120. try
  121. {
  122. lockManger.jc.del(key);
  123. LOGGER.debug("{}锁被{}释放 .", key, value);
  124. }
  125. catch (JedisConnectionException je)
  126. {
  127.  
  128. }
  129. catch (Exception e)
  130. {
  131.  
  132. }
  133. }
  134.  
  135. public void test() throws InterruptedException
  136.     {
  137.         String productId = "18061249844";
  138.         String userId;
  139.         for (int i = 1; i <= 500; i++)
  140.         {
  141.             //随机产生一个用户
  142.             userId = UUID.randomUUID().toString();
  143.             //该用户试图锁定(假设被锁,尝试等待300毫秒)。在处理一些事情后。再释放锁
  144.             testLock(productId, userId);
  145.             Thread.sleep(20);
  146.         }
  147.     }
  148.  
  149. private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(150, 150, 30L, TimeUnit.SECONDS,
  150. new LinkedBlockingQueue<Runnable>(), new BasicThreadFactory.Builder().daemon(true)
  151. .namingPattern("mongo-oper-%d").build(), new ThreadPoolExecutor.CallerRunsPolicy());
  152.  
  153. private static void testLock(final String key , final String value)
  154. {
  155. executor.execute(new Runnable()
  156. {
  157. @Override
  158. public void run()
  159. {
  160. try
  161. {
  162. //获取锁,假设不能马上获取,尝试等待1000毫秒
  163. boolean isLock = DistributedLockManger.tryLock(key, value, 500, TimeUnit.MILLISECONDS);
  164. if (isLock)
  165. {
  166. //doSomething(占用锁20毫秒到4秒,模拟处理事务)
  167. long doSomeThingTime = RandomUtils.nextLong(20, 4000);
  168. LOGGER.debug("{}将持有锁{}时长{}毫秒.", value, key, doSomeThingTime);
  169. Thread.sleep(doSomeThingTime);
  170. //然后释放锁
  171. DistributedLockManger.unLock(key, value);
  172. }
  173. }
  174. catch (Throwable th)
  175. {
  176. }
  177. }
  178. });
  179. }
  180.  
  181. public JedisCluster getJc()
  182. {
  183. return jc;
  184. }
  185.  
  186. public void setJc(JedisCluster jc)
  187. {
  188. this.jc = jc;
  189. }
  190. }

Spring配置:

redis.properties

  1. #最大闲置连接数
  2. im.hs.server.redis.maxIdle=500
  3. #最大连接数,超过此连接时操作redis会报错
  4. im.hs.server.redis.maxTotal=5000
  5. im.hs.server.redis.maxWaitTime=1000
  6. im.hs.server.redis.testOnBorrow=true
  7. #最小闲置连接数,spring启动的时候自己主动建立该数目的连接供应用程序使用,不够的时候会申请。
  8. im.hs.server.redis.minIdle=300

spring-redis.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
  6. xmlns:aop="http://www.springframework.org/schema/aop"
  7. xsi:schemaLocation="
  8. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  9. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
  10.  
  11. <context:property-placeholder location="classpath:conf/redis/redis.properties" />
  12.  
  13. <!-- 其实,仅仅须要连接1个节点就能够 -->
  14. <bean id="culster7001" class="redis.clients.jedis.HostAndPort">
  15. <constructor-arg name="host" value="192.168.62.153" />
  16. <constructor-arg name="port" value="7001" />
  17. </bean>
  18. <bean id="culster7002" class="redis.clients.jedis.HostAndPort">
  19. <constructor-arg name="host" value="192.168.62.153" />
  20. <constructor-arg name="port" value="7002" />
  21. </bean>
  22. <bean id="culster7003" class="redis.clients.jedis.HostAndPort">
  23. <constructor-arg name="host" value="192.168.62.154" />
  24. <constructor-arg name="port" value="7003" />
  25. </bean>
  26. <bean id="culster7004" class="redis.clients.jedis.HostAndPort">
  27. <constructor-arg name="host" value="192.168.62.154" />
  28. <constructor-arg name="port" value="7004" />
  29. </bean>
  30. <bean id="culster7005" class="redis.clients.jedis.HostAndPort">
  31. <constructor-arg name="host" value="192.168.62.155" />
  32. <constructor-arg name="port" value="7005" />
  33. </bean>
  34. <bean id="culster7006" class="redis.clients.jedis.HostAndPort">
  35. <constructor-arg name="host" value="192.168.62.155" />
  36. <constructor-arg name="port" value="7006" />
  37. </bean>
  38. <bean id="culster7007" class="redis.clients.jedis.HostAndPort">
  39. <constructor-arg name="host" value="192.168.62.152" />
  40. <constructor-arg name="port" value="7007" />
  41. </bean>
  42. <bean id="culster7008" class="redis.clients.jedis.HostAndPort">
  43. <constructor-arg name="host" value="192.168.62.152" />
  44. <constructor-arg name="port" value="7008" />
  45. </bean>
  46. <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
  47. <property name="maxTotal" value="${im.hs.server.redis.maxTotal}" />
  48. <property name="minIdle" value="${im.hs.server.redis.minIdle}" />
  49. <property name="maxWaitMillis" value="${im.hs.server.redis.maxWaitTime}" />
  50. <property name="maxIdle" value="${im.hs.server.redis.maxIdle}" />
  51. <property name="testOnBorrow" value="${im.hs.server.redis.testOnBorrow}" />
  52. <property name="testOnReturn" value="true" />
  53. <property name="testWhileIdle" value="true" />
  54. </bean>
  55.  
  56. <bean id="jc" class="redis.clients.jedis.JedisCluster">
  57. <constructor-arg name="nodes">
  58. <set>
  59. <ref bean="culster7001" />
  60. <ref bean="culster7002" />
  61. <ref bean="culster7003" />
  62. <ref bean="culster7004" />
  63. <ref bean="culster7005" />
  64. <ref bean="culster7006" />
  65. <ref bean="culster7007" />
  66. <ref bean="culster7008" />
  67. </set>
  68. </constructor-arg>
  69. <constructor-arg name="poolConfig"> <ref bean="poolConfig" /> </constructor-arg>
  70. </bean>
  71.  
  72. <bean id="distributedLock" class="com.gaojiasoft.gaojiaRedis.DistributedLockManger" />
  73.  
  74. <context:annotation-config />
  75. </beans>

測试代码:

  1. package com.gaojiasoft.gaojiaRedis;
  2.  
  3. import java.util.UUID;
  4. import java.util.concurrent.TimeUnit;
  5.  
  6. import junit.framework.TestCase;
  7.  
  8. import org.junit.Test;
  9. import org.slf4j.Logger;
  10. import org.slf4j.LoggerFactory;
  11. import org.springframework.context.ConfigurableApplicationContext;
  12. import org.springframework.context.support.ClassPathXmlApplicationContext;
  13.  
  14. public class ClusterJedisTester extends TestCase
  15. {
  16. private Logger logger = LoggerFactory.getLogger(ClusterJedisTester.class);
  17.  
  18. private static String[] list = new String[] { "classpath:conf/redis/spring-redis.xml" };
  19.  
  20. private static ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(list, true, null);
  21.  
  22. DistributedLockManger distributedLock = (DistributedLockManger)context.getBean("distributedLock");
  23.  
  24. @Test
  25. public void testLock() throws InterruptedException
  26. {
  27. distributedLock.test();
  28. while(true)
  29. {
  30. Thread.sleep(1000);
  31. }
  32. }
  33. }

基于redis集群实现的分布式锁,可用于秒杀商品的库存数量管理,有測试代码(何志雄)的更多相关文章

  1. 基于redis集群实现的分布式锁,可用于秒杀,定时器。

    在分布式系统中,经常会出现需要竞争同一资源的情况,使用redis可以实现分布式锁. 前提:redis集群已经整合项目,并且可以直接注入JedisCluster使用: @Autowired privat ...

  2. 分布式ID系列(4)——Redis集群实现的分布式ID适合做分布式ID吗

    首先是项目地址: https://github.com/maqiankun/distributed-id-redis-generator 关于Redis集群生成分布式ID,这里要先了解redis使用l ...

  3. springcloud微服务基于redis集群的单点登录

    springcloud微服务基于redis集群的单点登录 yls 2019-9-23 简介 本文介绍微服务架构中如何实现单点登录功能 创建三个服务: 操作redis集群的服务,用于多个服务之间共享数据 ...

  4. 【spring boot】【redis】spring boot基于redis的LUA脚本 实现分布式锁

    spring boot基于redis的LUA脚本 实现分布式锁[都是基于redis单点下] 一.spring boot 1.5.X 基于redis 的 lua脚本实现分布式锁 1.pom.xml &l ...

  5. 集群多JVM分布式锁实现

    基于数据库表乐观锁 (基本废弃) 要实现分布式锁,最简单的⽅方式可能就是直接创建⼀一张锁表,然后通过操作该表中的数据来实现了了. 当我们要锁住某个⽅法或资源时,我们就在该表中增加一条记录,想要释放锁的 ...

  6. Redis集群模式之分布式集群模式

    前言 Redis集群模式主要有2种: 主从集群 分布式集群. 前者主要是为了高可用或是读写分离,后者为了更好的存储数据,负载均衡. 本文主要讲解主从集群.本章主要讲解后一半部分,Redis集群. 与本 ...

  7. 【连载】redis库存操作,分布式锁的四种实现方式[四]--基于Redis lua脚本机制实现分布式锁

    一.redis lua介绍 Redis 提供了非常丰富的指令集,但是用户依然不满足,希望可以自定义扩充若干指令来完成一些特定领域的问题.Redis 为这样的用户场景提供了 lua 脚本支持,用户可以向 ...

  8. java-spring基于redis单机版(redisTemplate)实现的分布式锁+redis消息队列,可用于秒杀,定时器,高并发,抢购

    此教程不涉及整合spring整合redis,可另行查阅资料教程. 代码: RedisLock package com.cashloan.analytics.utils; import org.slf4 ...

  9. Couchbase集群和Redis集群解析

    Couchbase集群和Redis集群解析 首先,关于一些数据库或者是缓存的集群有两种结构,一种是Cluster;一种是master-salve. 关于缓存系统一般使用的就是Redis,Redis是开 ...

随机推荐

  1. C++类指针初始化

    上面的代码会打印“A”. C++ 类指针定义的时候没有初始化的时候,居然可以安全的调用类内部的成员函数而不出错. 在网上查了一下:   初始化为NULL的类指针可以安全的调用不涉及类成员变量的类成员函 ...

  2. HDU 4280 Island Transport

    Island Transport Time Limit: 10000ms Memory Limit: 65536KB This problem will be judged on HDU. Origi ...

  3. 4C. Stars

    4C. Stars Time Limit: 2000ms Case Time Limit: 2000ms Memory Limit: 65536KB   64-bit integer IO forma ...

  4. 转:WOM 编码与一次写入型存储器的重复使用

    转自:WOM 编码与一次写入型存储器的重复使用 (很有趣的算法设计)——来自 Matrix67: The Aha Moments 大神 计算机历史上,很多存储器的写入操作都是一次性的. Wikiped ...

  5. FreeMarker数据模板引擎全面教程mark

    http://blog.csdn.net/fhx007/article/details/7902040/#comments 以下内容全部是网上收集: FreeMarker的模板文件并不比HTML页面复 ...

  6. PHP实现当前文件夹下所有文件和文件夹的遍历

    <?php function myScandir($dir){ static $flag=''; //设置缩进显示格式 $files = scandir($dir);//读取当前文件夹的文件 $ ...

  7. 16.1113 模拟考试T2

    测试题 #4 括号括号[问题描述]有一个长度为?的括号序列,以及?种不同的括号.序列的每个位置上是哪种括号是随机的,并且已知每个位置上出现每种左右括号的概率.求整个序列是一个合法的括号序列的概率.我们 ...

  8. 【AIM Tech Round 5 (Div. 1 + Div. 2) 】

    A:https://www.cnblogs.com/myx12345/p/9844152.html B:https://www.cnblogs.com/myx12345/p/9844205.html ...

  9. flask-script插件

    首先在启动Flask项目时,我们可以传不同的参数作为运行参数.但是我们只能在入口app.run()传参.这样十分的不方便.Flask-Script 是一个 Flask 扩展,为 Flask 程序添加了 ...

  10. POSTMAN编写文档

    第一步:创建文件夹: 同时创建全局变量: 第二步:创建分组文件夹: 第三步:添加请求: 类似正常调试,然后多了一步保存: 保存: 请求方式发生相应变化,同时颜色也发生变化,说明保存成功: ====== ...