废话不多说,直接上代码。
细节可以看这个博客:漫画:什么是分布式锁?
package com.sheliming.jedis.lock;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;
import java.util.Collections;
public class RedisLock {
private static Jedis jedis;
static {
//创建一连接池对象
JedisPool jedisPool = new JedisPool("127.0.0.1", 6379);
//从连接池中获得连接
jedis = jedisPool.getResource();
}
/**
* 获取锁
*
* @param lockKey 锁的唯一表示
* @param threadId 请求的线程id,防止其他线程释放锁
* @param expireTime 锁的超时时间
* @return true:获取成功 false:获取失败
*/
public static boolean getLock(String lockKey, String threadId, int expireTime) {
//NX:保证互斥性
SetParams params = new SetParams();
params.ex(expireTime);
params.nx();
String result = jedis.set(lockKey, threadId, params);
if ("OK".equals(result)) {
return true;
}
return false;
}
/**
* 释放锁
*
* @param lockKey 锁的唯一表示
* @param threadId 请求的线程id,防止其他线程释放锁
* @return true:释放成功 false:释放失败
*/
public static boolean releaseLock(String lockKey, String threadId) {
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(threadId));
if (result.equals(1L)) {
return true;
}
return false;
}
public static void main(String[] args) {
boolean res1 = RedisLock.getLock("testlock", String.valueOf(Thread.currentThread().getId()), 20);
boolean res2 = RedisLock.getLock("testlock", String.valueOf(Thread.currentThread().getId()), 20);
System.out.println("res1:" + res1);
System.out.println("res2:" + res2);
boolean res3 = RedisLock.releaseLock("testlock", String.valueOf(Thread.currentThread().getId()));
System.out.println("res3:" + res3);
}
}
以上方法看上去很完美,但是还是存在几个问题:
1、如果过期时间到了,任务没有执行完,锁被释放了,其他线程拿到锁就会出现问题。
解决方法:增加一个守护线程,为锁续航。如果获得锁的线程节点挂了,守护线程也就不会续航了。
2、如果是主从的redis,如果加锁的时候写入的主节点,在还没有同步到从节点的时候主节点挂了,等从节点自动变为主节点的时候锁就丢失了。
解决方法:使用redis官方推荐的RedLock,底层相当于实现了一致性算法RAFT。代码繁琐,容易出错。 至少3个redis主从保证。
本质问题,redis是AP模型,而分布式锁是需要CP模型。需要一致性。
所以redis能不能用做分布式锁看业务场景,如果是交易类型就不能用,如果是社交等场景可以使用。
分布式锁的终极目标:
- 强一致性
- 服务高可用
- 锁的自动续约和自动释放
- 代码高度抽象,接入极简
- 可视化管理后台,监控及管理