一、使用RedisTemplate的简单实现
1.在Spring Boot应用程序中添加Redis依赖项
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.创建一个Redis分布式锁实现的类,示例代码:
@Serice
public class RedisLockSerice {
@Autowired
private RedisTemplate<String, String> redisTemplate;
public boolean lock(String key, String value, long expireTime) {
Boolean success = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.MILLISECONDS);
return success != null && success;
}
public void unlock(String key, String value) {
String currentValue = redisTemplate.opsForValue().get(key);
if (currentValue != null && currentValue.equals(value)) {
redisTemplate.delete(key);
}
}
}
3. 这种方案的缺陷:
锁的释放方式不够安全。在unlock() 方法中,只是简单地通过 RedisTemplate 的 delete() 方法删除了 Redis 中的 key-value 对,但是这种方式存在一定的风险,因为可能存在删除了其他线程获取的锁的情况。
二、使用RedisTemplate及Lua脚本的实现
1. 使用Lua脚本,创建一个Redis分布式锁实现的类,示例代码:
@Service
public class RedisLockService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
//获取锁的 Lua 脚本
private static final String LOCK_SCRIPT =
"if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then " +
"redis.call('pexpire', KEYS[1], ARGV[2]); " +
"return true; " +
"else return false; " +
"end";
//释放锁的 Lua 脚本
private static final String UNLOCK_SCRIPT =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
"redis.call('del', KEYS[1]); " +
"return true; " +
"else return false; " +
"end";
public boolean lock(String key, String value, Long expire) {
String[] keys = {key};
String[] args = {value, String.valueOf(expire)};
RedisScript<Boolean> script = new DefaultRedisScript<>(LOCK_SCRIPT, Boolean.class);
Boolean result = redisTemplate.execute(script, Arrays.asList(keys), args);
return result != null && result;
}
public boolean unlock(String key,String value){
String[] keys = {key};
String[] args = {value};
RedisScript<Boolean> script = new DefaultRedisScript<>(UNLOCK_SCRIPT, Boolean.class);
Boolean result = redisTemplate.execute(script, Arrays.asList(keys), args);
return result != null && result;
}
}
这种方案是使用Redis实现分布式锁比较常见的方式。
三、集成Redisson实现分布式锁
Redisson 是一个在 Redis 的基础上实现的 Java 驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的 Java 常用对象,还实现了可重入锁(Reentrant Lock)、公平锁(Fair Lock、联锁(MultiLock)、 红锁(RedLock)、 读写锁(ReadWriteLock)等,还提供了许多分布式服务。Redisson 提供了使用 Redis 的最简单和最便捷的方法。Redisson 的宗旨是促进使用者对 Redis 的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。
1. 在Spring Boot应用程序中添加Redisson依赖:
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>{redisson-version}</version>
</dependency>
2.创建一个Redisson分布式锁实现的类,示例代码:
@Service
@Slf4j
public class RedissonLockService {
@Autowired
private RedissonClient redissonClient;
/**
* 尝试在指定时间内获取分布式锁
* @param key
* @param waitTime (s)
* @param expire(ms)
* @return
*/
public boolean lock(String key, int waitTime, Long expire) {
RLock lock = redissonClient.getLock(key);
log.info("lock:" + lock);
try {
//lock.lock(expire, TimeUnit.MILLISECONDS); //lock 会一直阻塞
//lock.tryLock(expire, TimeUnit.MILLISECONDS); //tryLock 如果不指定WaitTime的話,也是阻塞模式
return lock.tryLock(waitTime, expire, TimeUnit.MILLISECONDS);
}
catch (InterruptedException e){
e.printStackTrace();
}
return false;
}
/**
* 释放分布式锁
* @param key
* @return
*/
public void unlock(String key){
RLock lock = redissonClient.getLock(key);
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
log.info("unlock come here...");
}
else{
log.info("no need to unlock...");
}
}
}
3.使用Redisson分布式加锁的三/四步:
第一步主要是获取RLock对象。
第二步尝试加锁,加锁失败,返回加锁失败。
第三步就是我们业务代码。
第四步释放锁。