Redis分布式锁有哪些

Redis分布式锁有哪些?这个问题可能是我们日常学习或工作经常见到的。希望通过这个问题能让你收获颇深。下面是小编给大家带来的参考内容,让我们一起来看看吧!

我们通常使用的synchronized或者Lock都是线程锁,对同一个JVM进程内的多个线程有效。因为锁的本质 是内存中存放一个标记,记录获取锁的线程是谁,这个标记对每个线程都可见。然而我们启动的多个订单服务,就是多个JVM,内存中的锁显然是不共享的,每个JVM进程都有自己的 锁,自然无法保证线程的互斥了,这个时候我们就需要使用到分布式锁了。常用的有三种解决方案:1.基于数据库实现 2.基于zookeeper的临时序列化节点实现 3.redis实现。本文我们介绍的就是redis的实现方式。
  实现分布式锁要满足3点:多进程可见,互斥,可重入。

1) 多进程可见

 redis本身就是基于JVM之外的,因此满足多进程可见的要求。

2) 互斥

 即同一时间只能有一个进程获取锁标记,我们可以通过redis的setnx实现,只有第一次执行的才会成功并返回1,其它情况返回0。

  

Redis分布式锁有哪些

 释放锁
 释放锁其实只需要把锁的key删除即可,使用del xxx指令。不过,如果在我们执行del之前,服务突然宕机,那么锁就永远无法删除了。所以我们可以通过setex 命令设置过期时间即可。

import java.util.UUID;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;/** * 第一种分布式锁 */@Componentpublic class RedisService {private final Logger log = LoggerFactory.getLogger(this.getClass()); @Autowired JedisPool jedisPool; // 获取锁之前的超时时间(获取锁的等待重试时间) private long acquireTimeout = 5000; // 获取锁之后的超时时间(防止死锁) private int timeOut = 10000; /** * 获取分布式锁 * @return 锁标识 */ public boolean getRedisLock(String lockName,String val) { Jedis jedis = null; try { jedis = jedisPool.getResource(); // 1.计算获取锁的时间 Long endTime = System.currentTimeMillis() + acquireTimeout; // 2.尝试获取锁 while (System.currentTimeMillis() < endTime) { // 3. 获取锁成功就设置过期时间 if (jedis.setnx(lockName, val) == 1) { jedis.expire(lockName, timeOut/1000); return true; } } } catch (Exception e) { log.error(e.getMessage()); } finally { returnResource(jedis); } return false; } /** * 释放分布式锁 * @param lockName 锁名称 */ public void unRedisLock(String lockName) { Jedis jedis = null; try { jedis = jedisPool.getResource(); // 释放锁 jedis.del(lockName); } catch (Exception e) { log.error(e.getMessage()); } finally { returnResource(jedis); } }// =============================================== public String get(String key) { Jedis jedis = null; String value = null; try { jedis = jedisPool.getResource(); value = jedis.get(key); log.info(value); } catch (Exception e) { log.error(e.getMessage()); } finally { returnResource(jedis); } return value; } public void set(String key, String value) { Jedis jedis = null; try { jedis = jedisPool.getResource(); jedis.set(key, value); } catch (Exception e) { log.error(e.getMessage()); } finally { returnResource(jedis); } } /** * 关闭连接 */ public void returnResource(Jedis jedis) { try { if(jedis!=null) jedis.close(); } catch (Exception e) { } } }

上面的分布式锁实现了,但是这时候还可能出现另外2个问题:
 一:获取锁时
  setnx获取锁成功了,还没来得及setex服务就宕机了,由于这种非原子性的操作,死锁又发生了。其实redis提供了 nx 与 ex连用的命令。

  

Redis分布式锁有哪些


 二:释放锁时
  1. 3个进程:A和B和C,在执行任务,并争抢锁,此时A获取了锁,并设置自动过期时间为10s
  2. A开始执行业务,因为某种原因,业务阻塞,耗时超过了10秒,此时锁自动释放了
  3. B恰好此时开始尝试获取锁,因为锁已经自动释放,成功获取锁
  4. A此时业务执行完毕,执行释放锁逻辑(删除key),于是B的锁被释放了,而B其实还在执行业务
  5. 此时进程C尝试获取锁,也成功了,因为A把B的锁删除了。
  问题出现了:B和C同时获取了锁,违反了互斥性!如何解决这个问题呢?我们应该在删除锁之前,判断这个锁是否是自己设置的锁,如果不是(例如自己 的锁已经超时释放),那么就不要删除了。所以我们可以在set 锁时,存入当前线程的唯一标识!删除锁前,判断下里面的值是不是与自己标识释放一 致,如果不一致,说明不是自己的锁,就不要删除了。

/** * 第二种分布式锁 */public class RedisTool { private static final String LOCK_SUCCESS = "OK"; private static final Long RELEASE_SUCCESS = 1L; /** * 尝试获取分布式锁 * @param jedis Redis客户端 * @param lockKey 锁 * @param requestId 请求标识 * @param expireTime 超期时间 * @return 是否获取成功 */ public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) { String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime); if (LOCK_SUCCESS.equals(result)) { return true; } return false; } /** * 释放分布式锁 * @param jedis Redis客户端 * @param lockKey 锁 * @param requestId 请求标识 * @return 是否释放成功 */ public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) { if (jedis.get(lockKey).equals(requestId)) { System.out.println("释放锁..." + Thread.currentThread().getName() + ",identifierValue:" + requestId); jedis.del(lockKey); return true; } return false; } }

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。