本文目录导读:

我来介绍Java实现Redis分布式锁的几种常见方案:
基于SETNX + EXPIRE (基础版)
import redis.clients.jedis.Jedis;
public class RedisDistributedLock {
private Jedis jedis;
private String lockKey;
private String lockValue;
private int expireTime = 3000; // 锁过期时间,单位毫秒
public RedisDistributedLock(Jedis jedis, String lockKey) {
this.jedis = jedis;
this.lockKey = lockKey;
this.lockValue = String.valueOf(System.currentTimeMillis());
}
/**
* 获取锁
*/
public boolean tryLock() {
// SET NX 命令:key不存在时才设置
String result = jedis.set(lockKey, lockValue, "NX", "PX", expireTime);
return "OK".equals(result);
}
/**
* 释放锁
*/
public void unlock() {
// 使用Lua脚本确保原子性操作
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else return 0 end";
jedis.eval(script, Collections.singletonList(lockKey),
Collections.singletonList(lockValue));
}
}
使用Redisson (推荐方案)
<!-- 添加依赖 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.17.7</version>
</dependency>
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import java.util.concurrent.TimeUnit;
public class RedissonLockService {
private RedissonClient redissonClient;
public RedissonLockService() {
Config config = new Config();
config.useSingleServer()
.setAddress("redis://127.0.0.1:6379")
.setPassword("password")
.setDatabase(0);
redissonClient = Redisson.create(config);
}
/**
* 使用Redisson实现分布式锁
*/
public void executeWithLock(String key, Runnable task) {
RLock lock = redissonClient.getLock(key);
try {
// 尝试获取锁,最多等待100秒,锁自动释放30秒
boolean isLocked = lock.tryLock(100, 30, TimeUnit.SECONDS);
if (isLocked) {
System.out.println("获取锁成功,执行业务逻辑");
task.run();
} else {
System.out.println("获取锁失败");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("锁操作被中断");
} finally {
// 释放锁(Redisson自动处理)
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
// 使用示例
public void businessMethod() {
executeWithLock("my_lock_key", () -> {
// 需要加锁的业务逻辑
System.out.println("执行业务操作...");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
}
使用Spring Data Redis + Lua脚本
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Component
public class SpringRedisLock {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// Lua脚本:原子性释放锁
private static final String UNLOCK_SCRIPT =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
/**
* 获取分布式锁
* @param lockKey 锁的key
* @param expireTime 过期时间(秒)
* @return 锁标识(用于释放锁)
*/
public String tryLock(String lockKey, long expireTime) {
String lockValue = UUID.randomUUID().toString();
Boolean success = redisTemplate.opsForValue()
.setIfAbsent(lockKey, lockValue, expireTime, TimeUnit.SECONDS);
if (Boolean.TRUE.equals(success)) {
return lockValue; // 返回锁标识
}
return null; // 获取锁失败
}
/**
* 释放分布式锁
* @param lockKey 锁的key
* @param lockValue 锁标识
* @return 是否释放成功
*/
public boolean unlock(String lockKey, String lockValue) {
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(UNLOCK_SCRIPT);
redisScript.setResultType(Long.class);
Long result = redisTemplate.execute(
redisScript,
Collections.singletonList(lockKey),
lockValue
);
return Long.valueOf(1).equals(result);
}
// 使用示例
public void businessWithLock() {
String lockKey = "business_lock";
String lockValue = tryLock(lockKey, 30);
if (lockValue != null) {
try {
// 获取锁成功,执行业务逻辑
System.out.println("执行业务操作...");
} finally {
// 释放锁
unlock(lockKey, lockValue);
}
} else {
System.out.println("获取锁失败,稍后重试");
}
}
}
完整的使用案例(订单防重复提交)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Jedis;
@Service
public class OrderService {
@Autowired
private JedisPool jedisPool;
/**
* 创建订单(防重复提交)
*/
public String createOrder(String userId, String orderId) {
String lockKey = "order_lock:" + userId;
String lockValue = orderId;
Jedis jedis = jedisPool.getResource();
try {
// 尝试获取锁,设置过期时间10秒
String result = jedis.set(lockKey, lockValue, "NX", "EX", 10);
if (!"OK".equals(result)) {
return "订单正在处理中,请勿重复提交";
}
// 检查是否重复提交(业务逻辑)
// ...
// 执行业务逻辑
Thread.sleep(3000);
return "订单创建成功";
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return "订单处理中断";
} finally {
// 安全释放锁
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else return 0 end";
jedis.eval(script,
Collections.singletonList(lockKey),
Collections.singletonList(lockValue));
if (jedis != null) {
jedis.close();
}
}
}
}
关键要点
- 原子性:使用Redis的NX + PX/EX参数确保原子操作
- 锁标识:使用UUID等唯一标识防止误删其他线程的锁
- 过期时间:设置合理的过期时间防止死锁
- Lua脚本:释放锁时使用Lua脚本确保原子性
- 推荐Redisson:生产环境建议使用Redisson,它提供了更完善的实现
注意事项
- 锁超时要根据业务执行时间合理设置
- 考虑Redis集群下的一致性问题
- 高并发场景下注意性能优化
- 业务代码做好重试和降级处理
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播