Redis分布式锁的实现方式分布式锁的实现方式(redis面试题面试题)
什么是分布式锁?什么是分布式锁?
要介绍分布式锁,首先要提到与分布式锁相对应的是线程锁、进程锁。
线程锁:主要用来给方法、代码块加锁。当某个方法或代码使用锁,在同一时刻仅有一个线程执行该方法或该代码段。线程锁
只在同一JVM中有效果,因为线程锁的实现在根本上是依靠线程之间共享内存实现的,比如synchronized是共享对象头,显示
锁Lock是共享某个变量(state)。
进程锁:为了控制同一操作系统中多个进程访问某个共享资源,因为进程具有独立性,各个进程无法访问其他进程的资源,因
此无法通过synchronized等线程锁实现进程锁。
分布式锁:当多个进程不在同一个系统中,用分布式锁控制多个进程对资源的访问。
前言前言
现在的业务场景越来越复杂,使用的架构也就越来越复杂,分布式、高并发已经是业务要求的常态。像腾讯系的不少服务,还
有CDN优化、异地多备份等处理。
说到分布式,就必然涉及到分布式锁的概念,如何保证不同机器不同线程的分布式锁同步呢?
实现要点实现要点
互斥性,同一时刻,智能有一个客户端持有锁。
防止死锁发生,如果持有锁的客户端崩溃没有主动释放锁,也要保证锁可以正常释放及其他客户端可以正常加锁。
加锁和释放锁必须是同一个客户端。
容错性,只有redis还有节点存活,就可以进行正常的加锁解锁操作。
正确的正确的redis分布式锁实现分布式锁实现
错误加锁方式错误加锁方式
错误方式一错误方式一
保证互斥和防止死锁,首先想到的使用redis的setnx命令保证互斥,为了防止死锁,锁需要设置一个超时时间。
public static void wrongLock(Jedis jedis, String key, String uniqueId, int expireTime) {
Long result = jedis.setnx(key, uniqueId);
if (1 == result) {
//如果该redis实例崩溃,那就无法设置过期时间了
jedis.expire(key, expireTime);
}
}
在多线程并发环境下,任何非原子性的操作,都可能导致问题。这段代码中,如果设置过期时间时,redis实例崩溃,就无法
设置过期时间。如果客户端没有正确的释放锁,那么该锁(永远不会过期),就永远不会被释放。
错误方式二错误方式二
比较容易想到的就是设置值和超时时间为原子原子操作就可以解决问题。那使用setnx命令,将value设置为过期时间不就ok了
吗?
public static boolean wrongLock(Jedis jedis, String key, int expireTime) {
long expireTs = System.currentTimeMillis() + expireTime;
// 锁不存在,当前线程加锁成果
if (jedis.setnx(key, String.valueOf(expireTs)) == 1) {
return true;
}
String value = jedis.get(key);
//如果当前锁存在,且锁已过期
if (value != null && NumberUtils.toLong(value) < System.currentTimeMillis()) {
//锁过期,设置新的过期时间
String oldValue = jedis.getSet(key, String.valueOf(expireTs));
if (oldValue != null && oldValue.equals(value)) {
// 多线程并发下,只有一个线程会设置成功
// 设置成功的这个线程,key的旧值一定和设置之前的key的值一致
return true;
}
}
// 其他情况,加锁失败
评论0