欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于Redis实现分布式锁

程序员文章站 2022-07-05 13:21:13
...


第三更,基于Redis实现的分布式锁,参考的是咕泡学院Mic老师的代码,我这里注释写的还是比较清楚了,我使用的是sprinboot,写这篇文章主要是需要的时候自己也可以翻看下。

Maven依赖

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.4.2</version>
        </dependency>

配置一个Jedis连接池

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

@Configuration
public class RedisPoolConfiguration {

    @Value("${jedis.host}")
    private String host;

    @Value("${jedis.port}")
    private int port;
	
    @Value("${jedis.maxIdle}")
    private int maxIdle;

    @Value("${jedis.maxTotal}")
    private int maxTotal;

    @Bean
    public JedisPool jedisPool(){
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxIdle(maxIdle);
        poolConfig.setMaxTotal(maxTotal);
        return new JedisPool(poolConfig,host,port);
    }
}

application.properties文件

jedis.host=localhost
jedis.port=6379
jedis.maxIdle=10
jedis.maxTotal=30

Redis 分布式锁

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;

import java.util.List;
import java.util.UUID;


@Component
public class RedisLock {

    private final JedisPool jedisPool;

    private static final Logger logger = LoggerFactory.getLogger(RedisLock.class);

    @Autowired
    public RedisLock(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    /**
     * 获得锁
     *
     * @param key     锁的键
     * @param timeout 获得锁的超时时间
     * @return 解锁用的唯一标识,超时返回<code>null</code>
     */
    public String getLock(String key, int timeout) {
        try {
            Jedis jedis = jedisPool.getResource();
            //定义锁的名称
            String lockKey = "redis_lock_" + key;
            //解锁用的唯一标识
            String value = UUID.randomUUID().toString();
            //获得锁的超时时间
            long end = System.currentTimeMillis() + timeout;
            //在超时时间内去竞争锁
            while (System.currentTimeMillis() < end) {
                //如果设置成功了就获得了锁,并设置过期时间,返回解锁的唯一标识
                if (jedis.setnx(lockKey, value) == 1) {
                //设置过期时间,注意这里不是原子操作,可能失败
                    jedis.expire(lockKey, timeout);
                    return value;
                }
                //while循环会执行很快,这里可以根据场景来适当调整或注释掉
                Thread.sleep(100);
                //如果在设置锁的时候redis挂了,但又没有设置过期时间,在这里就可以设置过期时间
                if (jedis.ttl(lockKey) == -1) {
                    jedis.expire(lockKey, timeout);
                }
            }
        } catch (Exception e) {
            logger.error("获得锁异常", e);
        }
        return null;
    }

    /**
     * 释放锁
     *
     * @param key   锁的键
     * @param value 解锁的唯一标识
     * @return
     */
    public boolean releaseLock(String key, String value) {
        try {
            Jedis jedis = jedisPool.getResource();
            String lockKey = "redis_lock_" + key;
            while (true) {
                //监控这个key,如果其他线程将key删除,下面事务中的代码不走
                jedis.watch(lockKey);
                //如果value和redis中的value一致,删除这个key,否则等待超时
                if (value.equals(jedis.get(lockKey))) {
                    //开启一个事务
                    Transaction transaction = jedis.multi();
                    //删除这个key
                    transaction.del(lockKey);
                    //执行事务
                    List<Object> list = transaction.exec();
                    //如果为空,就继续循环
                    if (list == null) {
                        continue;
                    }
                    return true;
                }
                //取消监控
                jedis.unwatch();
                //如果其他线程将key删除,直接退出循环,返回false
                break;
            }
        } catch (Exception e) {
            logger.error("释放锁异常", e);
        }
        return false;
    }
    
}