Redis 分布式锁的基本功能包括, 同一刻只能有一个人占有锁, 当锁被其他人占用时, 获取者可以等待他人释放锁, 此外锁本身必须能超时自动释放.
直接上 java 代码, 如下:
- package com.test;
- import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
- import Redis.clients.jedis.Jedis;
- import Redis.clients.jedis.JedisPool;
- import java.util.Arrays;
- import java.util.concurrent.TimeUnit;
- /**
- * 简单的单实例 Redis 分布式锁
- * 没有实现的高级功能: 锁的重入, 锁的续约等
- *
- * @Author:tt
- * @Description:
- * @CreateTime:2019/6/12
- */
- public class SingleRedisLock {
- private JedisPool jedisPool;
- /**
- * 获取锁
- *
- * @param lockKey 锁的 key
- * @param lockVal 锁的 val, 可以利用来实现 "避免误删别人锁","锁的重入" 等功能
- * @param lockMaxLifeTime 锁的最大生命时长, 到期自动销毁, 单位: 毫秒
- * @param tryWaitingTime 等待获取锁的超时时间, 单位: 毫秒
- * @param waitingSleepTime 等待获取锁的阻塞周期, 单位: 毫秒, 设置过短会造成 CPU 竞争, 设置过长会造成浪费, 需依赖于'具体业务平均的执行时长'
- * @return
- */
- public Boolean tryLock(String lockKey, String lockVal, int lockMaxLifeTime, int tryWaitingTime, int waitingSleepTime) {
- //lua 脚本, 让逻辑简单清晰, 同时保证原子性
- //setNX: 成功 - 1, 失败 - 0
- String lua = "if redis.call('set',KEYS[1],ARGV[1],'PX',ARGV[2],'NX') then return 1 else return 0 end";
- // 获取锁的开始时间
- Long tryBeginTime = System.currentTimeMillis();
- // 轮询
- while (true) {
- Long result = null;
- Jedis jedis = null;
- try {
- jedis = jedisPool.getResource();
- result = (Long) jedis.eval(lua, Arrays.asList(lockKey), Arrays.asList(lockVal, String.valueOf(lockMaxLifeTime)));
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (jedis != null) {
- try {
- jedis.close();
- } catch (Exception e) {
- }
- }
- }
- // 获取锁成功
- if (Long.valueOf(1).equals(result)) {
- return true;
- }
- // 当前时间
- Long now = System.currentTimeMillis();
- // 获取等待超时, 就不用获取了
- if (now - tryBeginTime>= tryWaitingTime) {
- return false;
- }
- try {
- // 阻塞等一会儿再重新去获取
- TimeUnit.MILLISECONDS.sleep(waitingSleepTime);
- } catch (InterruptedException e) {
- }
- }
- }
- /**
- * 释放锁
- *
- * @param lockKey
- * @param lockVal
- * @return
- */
- public void releaseLock(String lockKey, String lockVal) {
- // 如果 lockVal 是自己的再删除, 防止误删, 场景来源: 当前锁的持有者操作时间太长, 锁已经自动释放并被别人占有了
- String lua = "if redis.call('get', KEYS[1]) == ARGV[1] then redis.call('del', KEYS[1]) end";
- Jedis jedis = null;
- try {
- jedis = jedisPool.getResource();
- jedis.eval(lua, Arrays.asList(lockKey), Arrays.asList(lockVal));
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (jedis != null) {
- try {
- jedis.close();
- } catch (Exception e) {
- }
- }
- }
- }
- // 测试
- public static void main(String[] args) {
- // 连接池
- JedisPool jedisPool = new JedisPool(new GenericObjectPoolConfig(), "127.0.0.1", 6379, 2000, "test123");
- SingleRedisLock simpleRedisLock = new SingleRedisLock();
- simpleRedisLock.jedisPool = jedisPool;
- // 模拟 10 个并发
- for (int i = 0; i <10; i++) {
- new Thread(() -> {
- String lockKey = "TEST_LOCK_KEY";
- String threadName = Thread.currentThread().getName();
- // 获取锁
- Boolean locked = simpleRedisLock.tryLock(lockKey, threadName,
- 30000, 5000, 200);
- // 获取锁失败
- if (!locked) {
- System.err.println(">>>" + threadName + "获取锁失败");
- return;
- }
- // 获取锁成功, 模拟执行业务操作
- System.out.println(">>>" + threadName + "获取锁成功");
- doShortBusiness();
- //doLongBusiness();
- // 释放锁
- simpleRedisLock.releaseLock(lockKey, threadName);
- }).start();
- }
- try {
- TimeUnit.MILLISECONDS.sleep(60000);
- } catch (InterruptedException e) {
- }
- }
- // 短任务: 100 毫秒
- static void doShortBusiness() {
- try {
- TimeUnit.MILLISECONDS.sleep(100);
- } catch (InterruptedException e) {
- }
- }
- // 长任务: 3 秒
- static void doLongBusiness() {
- try {
- TimeUnit.MILLISECONDS.sleep(3000);
- } catch (InterruptedException e) {
- }
- }
- }
锁的高级功能包含锁的重入, 锁的续约等, 当然为了保证锁的高可用, Redis 还有主从, 集群等部署方式, 对应的锁的实现也有区别, 略微复杂, 不过有现成的框架可供我们参考使用, 比较知名的如 Redisson, 一个强大的 Redis 客户端, 当然包括对 "分布式锁" 的完美实现, 其支持 Redis 单实例, 哨兵, 集群等模式.
写在最后
来源: http://www.bubuko.com/infodetail-3099912.html