用 原生 Redis(Jedis、Lettuce) 实现分布式锁,可以参考 Redisson 的原理,但需要自己处理锁的自动续期、故障恢复等细节。核心思路是使用 Redis 的 SET NX EX
或 SET PX NX
命令来实现互斥锁,并利用 Lua 脚本 保障原子性。
实现思路
- 获取锁
SET key value NX PX expiration
,确保锁只能被一个线程获取,并设置过期时间。
- 自动续期
- 通过 后台线程 定时续期,防止业务执行时间过长导致锁超时释放。
- 释放锁
- 通过 Lua 脚本 保证原子性,只有 当前线程 持有锁时才能释放。
完整代码
1. 依赖引入
使用 Jedis 作为 Redis 客户端:
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.3.1</version>
</dependency>
2. 分布式锁实现
获取锁
import redis.clients.jedis.Jedis;
import java.util.UUID;public class RedisLock {private static final String LOCK_SUCCESS = "OK";private static final String LOCK_KEY = "my_lock";private static final int EXPIRE_TIME = 10_000; // 10秒过期private final Jedis jedis;private final String lockValue; // 唯一标识锁的持有者public RedisLock(Jedis jedis) {this.jedis = jedis;this.lockValue = UUID.randomUUID().toString(); // 防止误删}public boolean tryLock() {String result = jedis.set(LOCK_KEY, lockValue, "NX", "PX", EXPIRE_TIME);return LOCK_SUCCESS.equals(result); // 返回 true 代表加锁成功}
}
自动续期
如果业务执行时间超过 10s,锁会自动释放,所以需要定时续期:
import java.util.concurrent.*;public class LockRenewal {private static final int RENEWAL_INTERVAL = 5000; // 每5秒续期private final Jedis jedis;private final String lockValue;private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);public LockRenewal(Jedis jedis, String lockValue) {this.jedis = jedis;this.lockValue = lockValue;}public void startRenewal() {scheduler.scheduleAtFixedRate(() -> {if (lockValue.equals(jedis.get("my_lock"))) {jedis.pexpire("my_lock", 10_000);}}, 0, RENEWAL_INTERVAL, TimeUnit.MILLISECONDS);}public void stopRenewal() {scheduler.shutdown();}
}
释放锁
使用 Lua 脚本,确保只有 持有锁的线程 才能删除:
public void unlock() {String luaScript ="if redis.call('get', KEYS[1]) == ARGV[1] then " +" return redis.call('del', KEYS[1]) " +"else " +" return 0 " +"end";jedis.eval(luaScript, 1, "my_lock", lockValue);
}
3. 使用示例
Jedis jedis = new Jedis("localhost", 6379);
RedisLock redisLock = new RedisLock(jedis);
LockRenewal renewal = new LockRenewal(jedis, redisLock.lockValue);if (redisLock.tryLock()) {renewal.startRenewal(); // 开启自动续期try {// 执行业务逻辑System.out.println("获取到锁,执行业务...");Thread.sleep(15000); // 模拟业务执行时间超过锁的原始超时时间} catch (InterruptedException e) {e.printStackTrace();} finally {redisLock.unlock();renewal.stopRenewal(); // 停止续期}
} else {System.out.println("获取锁失败");
}
总结
- 互斥性:
SET NX PX
确保只有一个线程能获取锁。 - 自动续期:定期
PEXPIRE
延长锁的存活时间。 - 安全释放:Lua 脚本保证只有锁的持有者才能删除。