单机实现:
通过set命令加NX/PX参数实现加锁
jedis.set(lockKey, requestId, "NX", "PX", expireTime);
requestId:可为UUID,删除时使用
通过del命令解锁:
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Lua脚本调用保证命令的原子性
通过判断requestId(可为UUID),是否为本线程,防止其它线程误删。
代码如下:
import java.util.Collections;
import java.util.List;
/**
* 〈通过redis尝试获取分布式锁〉
*
* @author zhangshaolin
* @create 2018/2/8
* @since 1.0.0
*/
public class RedisLock {
/**
* 正确示例:获取分布式锁
*
* @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 ("OK".equals(result)) {
return true;
}
return false;
}
/**
* 错误示例
* 原因:两部操作不具有原子性
*
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @param expireTime 超期时间
* @return 是否获取成功
*/
public static boolean wrongGetLock1(Jedis jedis, String lockKey, String requestId, int expireTime) {
Long result = jedis.setnx(lockKey, requestId);
if (result == 1) {
// 若在这里程序突然崩溃,则无法设置过期时间,将发生死锁
jedis.expire(lockKey, expireTime);
return true;
}
return false;
}
/**
* 正确示例:释放分布式锁
*
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @return 是否释放成功
*/
public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
Long RELEASE_SUCCESS = 1L;
if (RELEASE_SUCCESS.equals(result)) {
return true;
}
return false;
}
/**
* 错误示例:释放分布式锁时两步操作,不是原子操作
*
* @param jedis
* @param lockKey
* @param requestId
* @return
*/
public static boolean wrongReleaseLock2(Jedis jedis, String lockKey, String requestId) {
// 判断加锁与解锁是不是同一个客户端
if (requestId.equals(jedis.get(lockKey))) {
// 若在此时,这把锁突然不是这个客户端的,则会误解锁
jedis.del(lockKey);
return true;
} else {
return false;
}
}
//为了便于演示,Jedis在此声明,实际Jedis的有开源的代码实现
private static class Jedis {
public Long setnx(String lockKey, String requestId
) {
return null;
}
public Object eval(String script, List strings, List strings1) {
return null;
}
public void expire(String lockKey, int expireTime) {
}
public String set(String lockKey, String requestId, String nx, String px, int expireTime) {
return null;
}
public String get(String lockKey) {
return null;
}
public void del(String lockKey) {
}
}
}
集群实现:
Redlock算法:类似paxos算法,拥有N个Redis master节点的集群,只有超过半数的结点获取锁成功后,认为锁成功。
这种方式要特别注意:锁超时时间要 大于 多数节点获取锁的时间
当发生在多数节点获取锁的时间大于锁超时时间时,获取到的锁在各节点早已超时失效,锁失效
Distributed locks with Redis – Redis
《Redis官方文档》用Redis构建分布式锁 | 并发编程网 – ifeve.com
spring boot redis分布式锁 - J猿的个人空间