在实际的工作中,有部分的特定场景需要使用到分布式锁来进行跨服务器资源的统一调配。之前在一家医疗互联网公司,因为黄牛抢号等原因,造成同一个患者同一时段在同一个医生处,挂到了两个及以上的号,我对之前我司实现的代码进行了封装和改进,在github上提供了源码,有需要的朋友,可以下载代码,并用maven打包成jar包,使用起来比较方便。

源码地址https://github.com/mantuliu/distributed

核心feature:

redis的setnx()方法,此方法提供了一个原子操作,可以保证有且只有一个分布式的调用返回值为1,在分布式锁的概念里,则代表此锁被此次调用的线程占用;

redis的expire()方法,通过此方法来设置此锁的过期时间;

对于死锁的情况,封装的分布式锁包可以自动解锁;

每次获取锁的线程,都会被标识,当此线程还没有释放锁时,此线程继续调用trylock方法,还可以获得该锁,只有获取该锁的线程才有unlock()释放锁的权利;

支持trylock()和trylock(long timeout, TimeUnit unit)两种方式;

使用应用服务器提供的jedis实例;

对于当前服务器试图获取该锁的线程数量进行监控,当数量大于阀值时,后续线程在trylock(long timeout, TimeUnit unit)及trylock()时,直接返回失败,阀值可以设置;

锁的过期时间默认值是5秒,可以根据实际情况进行设置;

锁的键值前缀默认值是mantu:dislock:,可以根据实际情况进行设置;

通过LockSupport类来获取锁,使得试图获取同一把锁的线程得到的对象是同一个。

源码解析:

   CommonType类

public class CommonType {

    public static int WAITLOCKERS = 2;//当前服务器等待锁的线程数量,如果超过或等于此值,当前线程直接返回,不再等待锁
public static String REDISKEY="mantu:dislock:";//redis下key前缀
public static int LOCKEXPIRETIME = 5;//锁的过期时间,单位秒,默认5秒过期
}

DisLock接口

public interface DisLock{

    boolean tryLock(Jedis jedis);
boolean tryLock(long time, TimeUnit unit,Jedis jedis) throws InterruptedException;
void unlock(Jedis jedis);
}

RedisDisLock类,实际的管理锁的类

public class RedisDisLock implements DisLock{
private static final Logger LOG = LoggerFactory.getLogger(RedisDisLock.class);
private transient Thread exclusiveOwnerThread; String lockKey="";
AtomicInteger waitToLock=new AtomicInteger(0); public RedisDisLock(String lockKey){
this.lockKey=CommonType.REDISKEY+lockKey;
} public boolean tryLock(Jedis jedis) {
Thread thread = Thread.currentThread();
if(thread==this.getExclusiveOwnerThread()){
return true;
}
Long i = jedis.setnx(lockKey, System.currentTimeMillis()+"");
if(i.intValue()==1){
jedis.expire(lockKey, CommonType.LOCKEXPIRETIME);
setExclusiveOwnerThread(thread);
return true;
}
else{//对于可能性非常低的死锁情况进行解锁
String initTime = jedis.get(lockKey);
if(initTime==null){
LOG.debug("initTime's value is null");
return false;
}
long iniTime=0L;
try{
iniTime = Long.parseLong(initTime);
}
catch(NumberFormatException nfex){
LOG.warn(nfex.getMessage());
jedis.expire(lockKey, 1);
return false;
}
if(((System.currentTimeMillis()-iniTime)/1000-CommonType.LOCKEXPIRETIME-1)>0){
String oldTime = jedis.getSet(lockKey, System.currentTimeMillis()+"");//对于及其极端的情况,lock被线程1处理掉了,但是又被线程2getset新的值了,通过下一次调用trylock()方法处理
if(oldTime==null){
LOG.info("oldTime is null");
return false;
}
if(initTime.equals(oldTime)){
release(jedis);
}
}
}
return false;
} public boolean tryLock(long timeout, TimeUnit unit,Jedis jedis) throws InterruptedException {
long nanosTimeout = unit.toNanos(timeout);
long lastTime = System.nanoTime();
if(tryLock(jedis)){
return true;
}
try{
int waitLockers = waitToLock.getAndIncrement();
if(waitLockers>=CommonType.WAITLOCKERS){
LOG.debug("wait the lock' thread num is much,so return flase");
return false;
}
for(;;){
if(tryLock(jedis)){
return true;
}
if (nanosTimeout <= 0){
LOG.debug("getlock timeout");
return false;
}
if(nanosTimeout>100000){
LockSupport.parkNanos(100000);//中断100毫秒
}
long now = System.nanoTime();
nanosTimeout -= now - lastTime;
lastTime = now;
if (nanosTimeout <= 0){
LOG.debug("getlock timeout");
return false;
}
if (Thread.interrupted()){
throw new InterruptedException();
}
}
}
finally{
waitToLock.decrementAndGet();
}
} public void unlock(Jedis jedis) {
Thread thread = Thread.currentThread();
if(thread==this.getExclusiveOwnerThread()){
LOG.debug("unlock the thread {}",thread.getId());
release(jedis);
}
} private void release(Jedis jedis){
setExclusiveOwnerThread(null);
jedis.del(lockKey);
} /**
* Sets the thread that currently owns exclusive access. A
* <tt>null</tt> argument indicates that no thread owns access.
* This method does not otherwise impose any synchronization or
* <tt>volatile</tt> field accesses.
*/
protected final void setExclusiveOwnerThread(Thread t) {
exclusiveOwnerThread = t;
} /**
* Returns the thread last set by
* <tt>setExclusiveOwnerThread</tt>, or <tt>null</tt> if never
* set. This method does not otherwise impose any synchronization
* or <tt>volatile</tt> field accesses.
* @return the owner thread
*/
protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
}

LockSupport类,实际的业务代码首先要通过LockSupport来获取redis锁的对象,再使用

public class LockSupport {

    static ConcurrentHashMap <String,RedisDisLock>lockMap = new ConcurrentHashMap<String,RedisDisLock>();

    public static DisLock getRedisLock(String lockKey){
RedisDisLock lock=null;
if(lockMap.contains(lockKey)){
lock = lockMap.get(lockKey);
}
else{
RedisDisLock lockN = new RedisDisLock(lockKey);
lock = lockMap.putIfAbsent(lockKey, lockN);
if(lock==null){
lock=lockN;
}
}
return lock;
}
}

RedisDisLockTest类是使用此jar的demo代码

public class RedisDisLockTest {

    public static void main(String [] args){
RedisDisLockTest test = new RedisDisLockTest();
//test.testOrder();
//test.testOrder2();
//test.testNOUnlock();
test.testOtherUnlock();
}
public void testOrder(){
JedisPool jp = new JedisPool("127.0.0.1",6379); for(int i=0;i<5;i++){
Jedis jedis = jp.getResource();
OrderThread th = new OrderThread("123456",jedis);
th.start();
}
}
public void testOrder2(){
JedisPool jp = new JedisPool("127.0.0.1",6379); for(int i=0;i<5;i++){
Jedis jedis = jp.getResource();
OrderThread th = new OrderThread("1234567",jedis);
th.start();
}
}
public void testNOUnlock(){
JedisPool jp = new JedisPool("127.0.0.1",6379); for(int i=0;i<5;i++){
Jedis jedis = jp.getResource();
TestNOUnlock th = new TestNOUnlock("12345678",jedis);
th.start();
}
}
public void testOtherUnlock(){
JedisPool jp = new JedisPool("127.0.0.1",6379); for(int i=0;i<5;i++){
Jedis jedis = jp.getResource();
TestOtherUnlock th = new TestOtherUnlock("unlock",jedis);
th.start();
}
}
class OrderThread extends Thread{
String lockKey="";
Jedis jedis;
public OrderThread(String lockKey,Jedis jedis){
this.lockKey=lockKey;
this.jedis=jedis;
}
public void run(){
DisLock lock = LockSupport.getRedisLock(lockKey);
try {
if(lock.tryLock(2,TimeUnit.SECONDS,jedis)){
System.out.println("订单"+lockKey+"创建成功!");
lock.unlock(jedis);
}
else{
System.out.println("没有成功获取到锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} class TestNOUnlock extends Thread{
String lockKey="";
Jedis jedis;
public TestNOUnlock(String lockKey,Jedis jedis){
this.lockKey=lockKey;
this.jedis=jedis;
}
public void run(){
DisLock lock = LockSupport.getRedisLock(lockKey);
try {
if(lock.tryLock(2,TimeUnit.SECONDS,jedis)){
System.out.println("订单"+lockKey+"创建成功!");
//lock.unlock(jedis);//no unlock
}
else{
System.out.println("没有成功获取到锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} class TestOtherUnlock extends Thread{
String lockKey="";
Jedis jedis;
public TestOtherUnlock(String lockKey,Jedis jedis){
this.lockKey=lockKey;
this.jedis=jedis;
}
public void run(){
DisLock lock = LockSupport.getRedisLock(lockKey);
if(lock.tryLock(jedis)){
System.out.println("订单"+lockKey+"创建成功!");
//lock.unlock(jedis);//no unlock
}
else{
lock.unlock(jedis);
System.out.println("TestOtherUnlock没有成功获取到锁");
} }
}
}

  

使用redis来实现分布式锁的更多相关文章

  1. 基于redis实现的分布式锁

    基于redis实现的分布式锁 我们知道,在多线程环境中,锁是实现共享资源互斥访问的重要机制,以保证任何时刻只有一个线程在访问共享资源.锁的基本原理是:用一个状态值表示锁,对锁的占用和释放通过状态值来标 ...

  2. 一个Redis实现的分布式锁

    import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.conne ...

  3. 基于Redis的简单分布式锁的原理

    参考资料:https://redis.io/commands/setnx 加锁是为了解决多线程的资源共享问题.Java中,单机环境的锁可以用synchronized和Lock,其他语言也都应该有自己的 ...

  4. redis客户端、分布式锁及数据一致性

    Redis Java客户端有很多的开源产品比如Redission.Jedis.lettuce等. Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持:Redis ...

  5. Redis系列(二)--分布式锁、分布式ID简单实现及思路

    分布式锁: Redis可以实现分布式锁,只是讨论Redis的实现思路,而真的实现分布式锁,Zookeeper更加可靠 为什么使用分布式锁: 单机环境下只存在多线程,通过同步操作就可以实现对并发环境的安 ...

  6. 在redis上实现分布式锁

    /** *在redis上实现分布式锁 */ class RedisLock { private $redisString; private $lockedNames = []; public func ...

  7. 如何用redis正确实现分布式锁?

    先把结论抛出来:redis无法正确实现分布式锁!即使是redis单节点也不行!redis的所谓分布式锁无法用在对锁要求严格的场景下,比如:同一个时间点只能有一个客户端获取锁. 首先来看下单节点下一般r ...

  8. redis系列:分布式锁

    redis系列:分布式锁 1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分 ...

  9. 一般实现分布式锁都有哪些方式?使用redis如何设计分布式锁?使用zk来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高?

    #(1)redis分布式锁 官方叫做RedLock算法,是redis官方支持的分布式锁算法. 这个分布式锁有3个重要的考量点,互斥(只能有一个客户端获取锁),不能死锁,容错(大部分redis节点创建了 ...

  10. Redis如何实现分布式锁

    今天我们来聊一聊分布式锁的那些事. 相信大家对锁已经不陌生了,我们在多线程环境中,如果需要对同一个资源进行操作,为了避免数据不一致,我们需要在操作共享资源之前进行加锁操作.在计算机科学中,锁(lock ...

随机推荐

  1. javascript 学习笔记之面向对象编程(二):继承&多态

    ~~接上篇~~上一篇实现了类的实现以及类成员变量和方法的定义,下面我们来了解下面向对象中两个最重要的特性:继承和多态. 继承 js中同样可以实现类的继承这一面向对象特性,继承父类中的所有成员(变量和属 ...

  2. php 获取客户端IP地址

    /** * 获取真实IP地址 */ /* 在PHP中getenv(参数)函数是一个用于获取环境变量的函数,根据提供不同的参数可以获取不同的环境变量, getenv("REMOTE_ADDR& ...

  3. 一些static_cast const_cast

    static_cast:干杂活的,那三个都有各自的专有用途,那三个不做的都由这个转型符来做,只要它能做的,用C语法的强制类型转换运算符也一定能够完成:但话又说回来了,C强制类型转换能做的,它可不一定都 ...

  4. WPF之application对象

    WPF:Application简介 Application是一个地址空间,在WPF中应用程序就是在System.Windows命名空间下的一个Application实例.一个应用程序只能对应一个App ...

  5. uboot的mtd功能支持

    一.概述 1.MTD MTD是Flash的一种管理方法,将Flash划分成几个分区,便于管理. u-boot的MTD功能是在文件cmd_jffs2.c中实现的,由此我们可以知道怎样打开u-boot的M ...

  6. iOS 细节 问题

    1.当一个空指针(nil pointer)调用了一个方法会发生什么? 安然无恙 —— 这是oc自带的消息机制,nil也能发送消息,而不会报错 2.为什么retainCount绝对不能用在发布的代码中? ...

  7. MySQL驱动阅读------executeQuery查询的过程,基于JDBC-----5.1.26

    Statement statement = connection.createStatement(); final ResultSet resultSet = statement.executeQue ...

  8. VS2012编译生成XP可以执行的程序

    首先需要的就是下载VS2012的Update 4更新包,然后打开项目的属性页,在 配置属性->平台工具集 选项中选择 Visual Studio 2012 - Windows XP (v110_ ...

  9. 超实用,你get了吗?再也不怕本地文件更新到环境用Linux命令重启服务器了。。。

    来公司这么久,写过不少代码,可是一碰见关于Linux命令操作的马上绕过,每次都是嚷嚷同事过来帮我替换文件,重启服务器,一直害怕接触命令的我一次一次不嫌麻烦,哈哈.有没有醉了?其实我一直都知道操作不难, ...

  10. 【itext】7步制作兼容各种文档格式的Itext5页眉页脚 实现page x pf y

    itext5页眉页脚工具类,实现page x of y 完美兼容各种格式大小文档A4/B5/B3,兼容各种文档格式自动计算页脚XY轴坐标 鉴于没人做的这么细致,自己就写了一个itext5页眉页脚工具类 ...