转载请标明出处。

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

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

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

package com.gaojiasoft.gaojiaRedis;

import java.util.UUID;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import javax.annotation.PostConstruct; import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.exceptions.JedisConnectionException; /**
* 分布式锁管理器,支持对单个资源加锁解锁。或给一批资源的批量加锁及解锁
* @ClassName: DistributedLockManger
* @Description:
* @author 何志雄 001
* @company 南京高嘉软件技术有限公司
* @date 2015年6月3日 下午5:44:06
*/
public class DistributedLockManger
{ private static final Logger LOGGER = LoggerFactory.getLogger(DistributedLockManger.class); private static final int DEFAULT_SINGLE_EXPIRE_TIME = 3; // private static final int DEFAULT_BATCH_EXPIRE_TIME = 6; //static的变量无法注解
@Autowired
JedisCluster jc; private static DistributedLockManger lockManger; public DistributedLockManger()
{
} @PostConstruct
public void init()
{
lockManger = this;
lockManger.jc = this.jc;
} /**
* 获取锁 假设锁可用 马上返回true。 否则马上返回false,作为非堵塞式锁使用
* @param key
* @param value
* @return
*/
public static boolean tryLock(String key , String value)
{
try
{
return tryLock(key, value, 0L, null);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
return false;
} /**
* 锁在给定的等待时间内空暇,则获取锁成功 返回true, 否则返回false。作为堵塞式锁使用
* @param key 锁键
* @param value 被谁锁定
* @param timeout 尝试获取锁时长。建议传递500,结合实践单位。则可表示500毫秒
* @param unit。建议传递TimeUnit.MILLISECONDS
* @return
* @throws InterruptedException
*/
public static boolean tryLock(String key , String value , long timeout , TimeUnit unit) throws InterruptedException
{
//纳秒
long begin = System.nanoTime();
do
{
//LOGGER.debug("{}尝试获得{}的锁.", value, key);
Long i = lockManger.jc.setnx(key, value);
if (i == 1)
{
lockManger.jc.expire(key, DEFAULT_SINGLE_EXPIRE_TIME);
LOGGER.debug("{}成功获取{}的锁,设置锁过期时间为{}秒 ", value, key, DEFAULT_SINGLE_EXPIRE_TIME);
return true;
}
else
{
// 存在锁 ,但可能获取不到,原因是获取的一刹那间
// String desc = lockManger.jc.get(key);
// LOGGER.error("{}正被{}锁定.", key, desc);
}
if (timeout == 0)
{
break;
}
//在其睡眠的期间,锁可能被解,也可能又被他人占用,但会尝试继续获取锁直到指定的时间
Thread.sleep(100);
}
while ((System.nanoTime() - begin) < unit.toNanos(timeout));
//因超时没有获得锁
return false;
} /**
* 释放单个锁
* @param key 锁键
* @param value 被谁释放
*/
public static void unLock(String key , String value)
{
try
{
lockManger.jc.del(key);
LOGGER.debug("{}锁被{}释放 .", key, value);
}
catch (JedisConnectionException je)
{ }
catch (Exception e)
{ }
} public void test() throws InterruptedException
    {
        String productId = "18061249844";
        String userId;
        for (int i = 1; i <= 500; i++)
        {
            //随机产生一个用户
            userId = UUID.randomUUID().toString();
            //该用户试图锁定(假设被锁,尝试等待300毫秒)。在处理一些事情后。再释放锁
            testLock(productId, userId);
            Thread.sleep(20);
        }
    } private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(150, 150, 30L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(), new BasicThreadFactory.Builder().daemon(true)
.namingPattern("mongo-oper-%d").build(), new ThreadPoolExecutor.CallerRunsPolicy()); private static void testLock(final String key , final String value)
{
executor.execute(new Runnable()
{
@Override
public void run()
{
try
{
//获取锁,假设不能马上获取,尝试等待1000毫秒
boolean isLock = DistributedLockManger.tryLock(key, value, 500, TimeUnit.MILLISECONDS);
if (isLock)
{
//doSomething(占用锁20毫秒到4秒,模拟处理事务)
long doSomeThingTime = RandomUtils.nextLong(20, 4000);
LOGGER.debug("{}将持有锁{}时长{}毫秒.", value, key, doSomeThingTime);
Thread.sleep(doSomeThingTime);
//然后释放锁
DistributedLockManger.unLock(key, value);
}
}
catch (Throwable th)
{
}
}
});
} public JedisCluster getJc()
{
return jc;
} public void setJc(JedisCluster jc)
{
this.jc = jc;
}
}

Spring配置:

redis.properties

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

spring-redis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="classpath:conf/redis/redis.properties" /> <!-- 其实,仅仅须要连接1个节点就能够 -->
<bean id="culster7001" class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.62.153" />
<constructor-arg name="port" value="7001" />
</bean>
<bean id="culster7002" class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.62.153" />
<constructor-arg name="port" value="7002" />
</bean>
<bean id="culster7003" class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.62.154" />
<constructor-arg name="port" value="7003" />
</bean>
<bean id="culster7004" class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.62.154" />
<constructor-arg name="port" value="7004" />
</bean>
<bean id="culster7005" class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.62.155" />
<constructor-arg name="port" value="7005" />
</bean>
<bean id="culster7006" class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.62.155" />
<constructor-arg name="port" value="7006" />
</bean>
<bean id="culster7007" class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.62.152" />
<constructor-arg name="port" value="7007" />
</bean>
<bean id="culster7008" class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.62.152" />
<constructor-arg name="port" value="7008" />
</bean>
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="${im.hs.server.redis.maxTotal}" />
<property name="minIdle" value="${im.hs.server.redis.minIdle}" />
<property name="maxWaitMillis" value="${im.hs.server.redis.maxWaitTime}" />
<property name="maxIdle" value="${im.hs.server.redis.maxIdle}" />
<property name="testOnBorrow" value="${im.hs.server.redis.testOnBorrow}" />
<property name="testOnReturn" value="true" />
<property name="testWhileIdle" value="true" />
</bean> <bean id="jc" class="redis.clients.jedis.JedisCluster">
<constructor-arg name="nodes">
<set>
<ref bean="culster7001" />
<ref bean="culster7002" />
<ref bean="culster7003" />
<ref bean="culster7004" />
<ref bean="culster7005" />
<ref bean="culster7006" />
<ref bean="culster7007" />
<ref bean="culster7008" />
</set>
</constructor-arg>
<constructor-arg name="poolConfig"> <ref bean="poolConfig" /> </constructor-arg>
</bean> <bean id="distributedLock" class="com.gaojiasoft.gaojiaRedis.DistributedLockManger" /> <context:annotation-config />
</beans>

測试代码:

package com.gaojiasoft.gaojiaRedis;

import java.util.UUID;
import java.util.concurrent.TimeUnit; import junit.framework.TestCase; import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class ClusterJedisTester extends TestCase
{
private Logger logger = LoggerFactory.getLogger(ClusterJedisTester.class); private static String[] list = new String[] { "classpath:conf/redis/spring-redis.xml" }; private static ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(list, true, null); DistributedLockManger distributedLock = (DistributedLockManger)context.getBean("distributedLock"); @Test
public void testLock() throws InterruptedException
{
distributedLock.test();
while(true)
{
Thread.sleep(1000);
}
}
}

基于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. [Kubernetes]Pod字段自动填充

    PodPreset(Pod预设置)在Kubernetes v1.11以后出现,开发人员只需要提交一个基本的Pod YAML,Kubernetes就可以自动给对应的Pod对象加上运维人员设定好的其他必要 ...

  2. 九度oj 题目1201:二叉排序树

    题目描述: 输入一系列整数,建立二叉排序数,并进行前序,中序,后序遍历. 输入: 输入第一行包括一个整数n(1<=n<=100).    接下来的一行包括n个整数. 输出: 可能有多组测试 ...

  3. tomcat在centos6+上的自启动脚本

    #!/bin/bash # # tomcat startup script for the Tomcat server # # chkconfig: 345 80 20 # description: ...

  4. iOS学习笔记13-网络(二)NSURLSession

    在2013年WWDC上苹果揭开了NSURLSession的面纱,将它作为NSURLConnection的继任者.现在使用最广泛的第三方网络框架:AFNetworking.SDWebImage等等都使用 ...

  5. P1373 小a和uim之大逃离 (动态规划)

    题目背景 小a和uim来到雨林中探险.突然一阵北风吹来,一片乌云从北部天边急涌过来,还伴着一道道闪电,一阵阵雷声.刹那间,狂风大作,乌云布满了天空,紧接着豆大的雨点从天空中打落下来,只见前方出现了一个 ...

  6. 【树状数组区间修改单点查询+分组】HDU 4267 A Simple Problem with Integers

    http://acm.hdu.edu.cn/showproblem.php?pid=4267 [思路] 树状数组的区间修改:在区间[a, b]内更新+x就在a的位置+x. 然后在b+1的位置-x 树状 ...

  7. bzoj1584 [Usaco2009 Mar]Cleaning Up 打扫卫生 动态规划+思维

    Description 有N头奶牛,每头那牛都有一个标号Pi,1 <= Pi <= M <= N <= 40000.现在Farmer John要把这些奶牛分成若干段,定义每段的 ...

  8. ElasticSearch分词器

    什么是分词器? 分词器,是将用户输入的一段文本,分析成符合逻辑的一种工具.到目前为止呢,分词器没有办法做到完全的符合人们的要求.和我们有关的分词器有英文的和中文的.英文的分词器过程:输入文本-关键词切 ...

  9. 使用selenium抓取淘宝的商品信息

    淘宝的页面大量使用了js加载数据,所以采用selenium来进行爬取更为简单,selenum作为一个测试工具,主要配合无窗口浏览器phantomjs来使用. import re from seleni ...

  10. HDU 5636 Shortest Path(Floyd)

    题目链接  HDU5636 n个点,其中编号相邻的两个点之间都有一条长度为1的边,然后除此之外还有3条长度为1的边. m个询问,每次询问求两个点之前的最短路. 我们把这三条边的6个点两两算最短路, 然 ...