分布式锁

一.乐观锁用于分布式锁:

多数是基于数据版本(version)的记录机制实现的。何谓数据版本号?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表添加一个 “version”字段来实现读取出数据时,将此版本号一同读出,之后更新时,对此版本号加1。

二.基于redis的分布式锁:

SETNX命令(SET If Not Exists):原子性操作,当且仅当key不存在时,将key的值设为value,并返回1;若给定的key存在时,则SETNX不做任何动作,并返回0

Expire命令:expire(key,expireTime)    key设置过期时间

GETSET命令:GETSET key value  将给定的key值设为value,并返回key的旧值,当key不存在时返回nil

(1)使用redis的setnx()、expire()方法,用于分布式锁

  • setnx(lockkey, 1) 如果返回0,则说明占位失败;如果返回1,则说明占位成功
  • expire()命令对lockkey设置超时时间,为的是避免死锁问题。
  • 执行完业务代码后,可以通过delete命令删除key

这个方案其实是可以解决日常工作中的需求的,但从技术方案的探讨上来说,可能还有一些可以完善的地方。比如,如果在第一步setnx执行成功后,在expire()命令执行成功前,发生了宕机的现象,那么就依然会出现死锁的问题

(2)使用redis的setnx()、get()、getset()方法,用于分布式锁

  • setnx(lockkey, 当前时间+过期超时时间) ,如果返回1,则获取锁成功;如果返回0则没有获取到锁,转向2。
  • get(lockkey)获取值oldExpireTime ,并将这个value值与当前的系统时间进行比较,如果小于当前系统时间,则认为这个锁已经超时,可以允许别的请求重新获取,转向3。
  • 计算newExpireTime=当前时间+过期超时时间,然后getset(lockkey, newExpireTime) 会返回当前lockkey的值currentExpireTime。
  • 判断currentExpireTime与oldExpireTime 是否相等,如果相等,说明当前getset设置成功,获取到了锁。如果不相等,说明这个锁又被别的请求获取走了,那么当前请求可以直接返回失败,或者继续重试。
  • 在获取到锁之后,当前线程可以开始自己的业务处理,当处理完毕后,比较自己的处理时间和对于锁设置的超时时间,如果小于锁设置的超时时间,则直接执行delete释放锁;如果大于锁设置的超时时间,则不需要再锁进行处理。
  • 可借鉴https://my.oschina.net/u/1995545/blog/366381
import cn.com.tpig.cache.redis.RedisService;
import cn.com.tpig.utils.SpringUtils;
/**
 * Created by IDEA
 * User: shma1664
 * Date: 2016-08-16 14:01
 * Desc: redis分布式锁
 */
public final class RedisLockUtil {
     private static final int defaultExpire = 60;
     private RedisLockUtil() {
     //
     }
     /**
     * 加锁
     * @param key redis key
     * @param expire 过期时间,单位秒
     * @return true:加锁成功,false,加锁失败
     */
     public static boolean lock(String key, int expire) {
         RedisService redisService = SpringUtils.getBean(RedisService.class);
         long status = redisService.setnx(key, "1");
         if(status == 1) {
             redisService.expire(key, expire);
             return true;
         }
         return false;
    }
     public static boolean lock(String key) {
         return lock2(key, defaultExpire);
     }
     /**
     * 加锁
     * @param key redis key
     * @param expire 过期时间,单位秒
     * @return true:加锁成功,false,加锁失败
     */
     public static boolean lock2(String key, int expire) {
         RedisService redisService = SpringUtils.getBean(RedisService.class);
         long value = System.currentTimeMillis() + expire;
         long status = redisService.setnx(key, String.valueOf(value));
         if(status == 1) {
             return true;
         }
         long oldExpireTime = Long.parseLong(redisService.get(key, "0"));
         if(oldExpireTime < System.currentTimeMillis()) {
             //超时
             long newExpireTime = System.currentTimeMillis() + expire;
             long currentExpireTime = Long.parseLong(redisService.getSet(key,                     String.valueOf(newExpireTime)));
             if(currentExpireTime == oldExpireTime) {
                 return true;
             }
         }
         return false;
     }
     public static void unLock1(String key) {
         RedisService redisService = SpringUtils.getBean(RedisService.class);
         redisService.del(key);
     }
     public static void unLock2(String key) { 
         RedisService redisService = SpringUtils.getBean(RedisService.class); 
         long oldExpireTime = Long.parseLong(redisService.get(key, "0")); 
         if(oldExpireTime > System.currentTimeMillis()) { 
             redisService.del(key); 
         }
     }
}

三.基于zk的分布式锁

后补

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值