Java 正确实现 redis 分布式锁

Java 正确实现 redis 分布式锁

1 源起

因为项目中有需要一个需求 ,就是在分布式的环境下 ,要根据一个 key,去获取一把分布式锁。所以。。。。。

2 我想要的效果

我想要的其实很简单, 就是根据一个 key 获取一个 分布式锁就行了 比如这样

// 获取分布式锁对象
RedisDistributeLock locker = new DefaultRedisDistributeLock();

// 锁定
locker.lock(jedis, "test1");

// TODO 业务逻辑

// 解锁
locker.release(jedis, "test1");

3 撸起袖子开干

3.1 导入 jedis 依赖

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

3.2 RedisDistributeLock 接口

RedisDistributeLock 定义了一些分布式锁的接口 实现, 本人能力有限,只实现了 非公平锁

package org.study.distributed_look.redis.way2;

import redis.clients.jedis.Jedis;

/** * @Author: huangwenjun * @Description: * @Date: Created in 13:45 2018/5/22 **/
public interface RedisDistributeLock {

    /** * 公平锁, 只能说 本机的公平 * @param jedis * @param key * @param uuid */
    void fairLock(Jedis jedis, String key, String uuid);

    /** * 非公平锁 * @param jedis * @param key * @param uuid */
    void unfairLock(Jedis jedis, String key, String uuid);

    /** * 锁 默认非公平 * @param jedis * @param key * @param uuid */
    void lock(Jedis jedis, String key, String uuid);

    /** * 解锁 * @param jedis * @param key * @param uuid */
    void release(Jedis jedis, String key, String uuid);
}

3.3 DefaultRedisDistributeLock

DefaultRedisDistributeLock 是我封装的 默认的分布式锁的实现

package org.study.distributed_look.redis.way2;

import org.study.distributed_look.redis.RedisTool;
import redis.clients.jedis.Jedis;

/** * @Author: huangwenjun * @Description: * @Date: Created in 13:48 2018/5/22 **/
public class DefaultRedisDistributeLock implements RedisDistributeLock {

    /** * 默认 非公平锁 */
    private static final Boolean DEFALUT_FAIR = false;

    /** * 过期时间 默认 10 秒, 太短会导致锁不住, 如果业务无法在指定过期时间内 完成, 则必须加长过期时间 */
    private static final Integer DEFAULT_EXPIRE_TIME = 10000;

    private Boolean isFair;

    private Integer expireTime;

    public DefaultRedisDistributeLock() {
        isFair = DEFALUT_FAIR;
        expireTime = DEFAULT_EXPIRE_TIME;
    }

    public DefaultRedisDistributeLock(boolean isFair, Integer expireTime) {
        isFair = isFair;
        expireTime = expireTime;
    }

    @Override
    public void lock(Jedis jedis, String key, String uuid) {
        if (isFair) {
            fairLock(jedis, key, uuid);
        } else {
            unfairLock(jedis, key, uuid);
        }
    }

    @Override
    public void fairLock(Jedis jedis, String key, String uuid) {
        // 通过一个队列维护, 参照 AQS 实现
    }

    @Override
    public void release(Jedis jedis, String key, String uuid) {
        try {
            while (true) {
                boolean released = RedisTool.releaseDistributedLock(jedis, key, uuid);

                if (released) {
                    break;
                }
            }
        } finally {
            jedis.close();
        }
    }

    @Override
    public void unfairLock(Jedis jedis, String key, String uuid) {
        while (true) {
            boolean locked = RedisTool.tryGetDistributedLock(jedis, key, uuid, expireTime);

            if (locked) {
                break;
            }
        }
    }
}

3.4 RedisTool

RedisTool 是我参照网上一个大神的代码, 正确的实现了 redis 分布式锁的 获取 和 释放

package org.study.distributed_look.redis;

import redis.clients.jedis.Jedis;

import java.util.Collections;

/** * * redis 分布式锁实现 * * @Author: huangwenjun * @Description: * @Date: Created in 15:00 2018/5/21 **/
public class RedisTool {

    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";

    private static final Long RELEASE_SUCCESS = 1L;

    /** * 尝试获取分布式锁 * * @param jedis Redis客户端 * @param lockKey 锁 * @param requestId 请求标识 * @param expireTime 超期时间 * @return 是否获取成功 */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);

        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }

        return false;
    }

    /** * 释放分布式锁 * @param jedis Redis客户端 * @param lockKey 锁 * @param requestId 请求标识 * @return 是否释放成功 */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
        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(requestId));

        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }

        return false;
    }
}

4 必须有测试啊!!!

4.1 测试代码

package org.study.distributed_look.redis.way2;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

/** * @Author: huangwenjun * @Description: * @Date: Created in 14:10 2018/5/22 **/
public class TestRedisDistributeLock {

    public static void main(String[] args) {
        for (int i = 1; i < 10; i ++) {
            new TestLock().start();
        }
    }

    static class TestLock extends Thread {

        static RedisDistributeLock locker = new DefaultRedisDistributeLock();

        JedisPool jedisPool = new JedisPool();

        @Override
        public void run() {
            Jedis jedis = jedisPool.getResource();
            locker.lock(jedis, "test1", "qwerqwer");

            // TODO 模拟业务
            System.out.println(Thread.currentThread().getName() + " get lock.....");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " release lock.....");

            locker.release(jedis, "test1", "qwerqwer");
        }
    }
}

4.2 输出

Thread-4 get lock.....
Thread-4 release lock.....
Thread-2 get lock.....
Thread-2 release lock.....
Thread-7 get lock.....
Thread-7 release lock.....
Thread-9 get lock.....
Thread-9 release lock.....
Thread-0 get lock.....
Thread-0 release lock.....
Thread-8 get lock.....
Thread-8 release lock.....
Thread-5 get lock.....
Thread-5 release lock.....
Thread-6 get lock.....
Thread-6 release lock.....
Thread-3 get lock.....
Thread-3 release lock.....

4.3 优化策略

一些优化策略

  • 1 这里为了简单起见, 没有优化 jedis 的连接池, 生产环境 必须手动设置 jedis 连接池
  • 2 locker.lock(jedis, “test1”, “qwerqwer”); 第三个 参数可以用 uuid, 至于为什么要有第三个参数 参见

https://www.cnblogs.com/linjiqin/p/8003838.html

5 整合到业务中

// 获取分布式锁对象
RedisDistributeLock locker = new DefaultRedisDistributeLock();

// 锁定
locker.lock(jedis, "test1", "uuid");

// TODO 业务逻辑

// 解锁
locker.release(jedis, "test1", "uuid");

和之前设想的基本一致, 如果有人发现问题 欢迎指正

    原文作者:java锁
    原文地址: https://blog.csdn.net/qq_35704236/article/details/80406916
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞