Java实现Redis分布式锁

1、背景:

在多线程环境下,通常会使用锁来保证有且只有一个线程来操作共享资源。比如:

object obj = new object();
lock (obj) 

//操作共享资源 
}

利用操作系统提供的锁机制,可以确保多线程或多进程下的并发唯一操作。但如果在多机环境下就不能满足了,当A,B两台机器同时操作C机器的共享资源时,就需要第三方的锁机制来保证在分布式环境下的资源协调,也称分布式锁。

在分布式环境下(多节点主机)加锁处理场景,如:秒杀、全局递增ID等等,需要对资源(变量)同步互斥。大部分的解决方案是基于DB实现的,Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系。由于Redis是单线程模型,命令操作原子性,所以利用这个特性可以很容易的实现分布式锁。


2、Redis有三个最基本属性来保证分布式锁的有效实现:

安全性: 互斥,在任何时候,只有一个客户端能持有锁。
活跃性A:没有死锁,即使客户端在持有锁的时候崩溃,最后也会有其他客户端能获得锁,超时机制。

活跃性B:故障容忍,只有大多数Redis节点时存活的,客户端仍可以获得锁和释放锁。

使用Redis实现分布式锁,有两个重要函数需要介绍

1)SETNX命令(SET if Not eXists)
语法:
SETNX key value
功能:
当且仅当 key 不存在,将 key 的值设为 value ,并返回1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0。


2)GETSET命令
语法:
GETSET key value
功能:
将给定 key 的值设为 value ,并返回 key 的旧值 (old value),当 key 存在但不是字符串类型时,返回一个错误,当key不存在时,返回nil。


3、代码:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package ct.tool;  
  2.   
  3. import redis.clients.jedis.Jedis;  
  4.   
  5. public class RedisDisLock {  
  6.       
  7.     private static final long expired = 1000;//1秒超时  
  8.     //上锁  
  9.     public static boolean acquireLock(Jedis jedis,String lock) {  
  10.         // 1. 通过SETNX试图获取一个lock  
  11.         boolean success = false;  
  12.         long value = System.currentTimeMillis() + expired + 1;        
  13.         long acquired = jedis.setnx(lock, String.valueOf(value));  
  14.         jedis.expire(lock, 1);//设置1秒超时  
  15.         //SETNX成功,则成功获取一个锁  
  16.         if (acquired == 1)  success = true;  
  17.         //SETNX失败,说明锁被其他客户端保持,检查其是否已经超时  
  18.         /*else { 
  19.             long oldValue = Long.valueOf(jedis.get(lock));          
  20.             if (oldValue < System.currentTimeMillis()) {//超时 
  21.                 //获取上一个锁到期时间,并设置现在的锁到期时间, 
  22.                 //只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的 
  23.                 String getValue = jedis.getSet(lock, String.valueOf(value)); 
  24.                 if (getValue !=null) { 
  25.                     if (Long.valueOf(getValue) == oldValue)  
  26.                         success = true;  
  27.                     else success = false;// 已被其他进程捷足先登了 
  28.                 }             
  29.             }else //未超时,则直接返回失败 
  30.                 success = false; 
  31.         }        */  
  32.         return success;        
  33.     }  
  34.        
  35.     //释放锁  
  36.     public static void releaseLock(Jedis jedis,String lock) {      
  37.         //long current = System.currentTimeMillis();         
  38.         // 避免删除非自己获取得到的锁  
  39.         //if (current < Long.valueOf(jedis.get(lock)))  
  40.             jedis.del(lock);   
  41.     }  
  42.   
  43. }  


代码应用中,要共享的代码段前加:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //枷锁  
  2.             boolean lockFlag=true;  
  3.             while(lockFlag){//循环等待拿锁  
  4.                 if (RedisDisLock.acquireLock(jd,"o2o")) lockFlag=false;  
  5.             }  

业务处理后,释放:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. RedisDisLock.releaseLock(jd, "o2o");  
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值