原创

redis分布式锁创建及使用教程

分布式锁

锁是什么我们当然知道,在多线程程序中,不予许多个线程同时操作某个变量或者同时执行某一代码块,我们就需要用锁来实现。在Java中,可以用synchronized或Lock接口的实现类来实现。那么什么是分布式锁呢?当我们的应用通过分布式部署,每个应用部署在不同的机器上,但是我们要保证这些不同机器上的同一方法在同一时间不能被多个线程执行,这时候就要用到分布式锁。分布式锁有很多种实现方式,这里我们介绍Redis实现方式。

前期准备

首先去redis官网下载redis,将文件解压,运行redis-server.exe启动redis服务。新建一个SpringBoot项目,添加redis依赖

SpringBoot项目,添加redis依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

在application.yml配置文件加上redis配置

spring:
    redis:
      host: 127.0.0.1
      port: 6379

1.利用Redisson来创建

Redisson是spring封装的分布式锁的使用工具,比较方便使用

  1. 导入依赖
        <!--redisson-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.10.6</version>
        </dependency>
  1. 配置redisson
@Configuration
public class RedisConfig {
    @Bean
    // RedisProperties 配置属性来源于redis中,在配置好redis好后,会自动从Spring
    //容器中获取对应的参数 例如url地址,还有对应的端口
    public RedissonClient redisClient(RedisProperties prop) {
        //占位符字符串
        String address = "redis://%s:%d";
        //创建配置类对象
        Config config = new Config();
        //String.format(a,b,c) 将用b,c 来替换a中的占位符
        config.useSingleServer().setAddress(String.format(address,prop.getHost(),prop.getPort()));
        //用redisson来创建RedissonClient
        return Redisson.create(config);
    }
}
  1. 使用redisson来加锁释放锁
@Component
@Slf4j
public class TestDemo {
    @Autowired
    private RedissonClient redissonClient;
    private static final String LOCK_KEY = "DEMO_TEST_LOCK";
    //测试加锁方法
    public void demo() {
        //1. 获取锁 (参数需要作为锁的字符串)
        RLock lock = redissonClient.getLock(LOCK_KEY);
        try{
            //获取锁有没有成功,
            //参数1 :获得锁的最长时间 参数2 结束时间 参数3 时间的单位
            boolean flag = lock.tryLock(0, 120, TimeUnit.MILLISECONDS);
            //如果获取到锁
            if(flag){
                log.info("获取到锁,开始执行");
            }
        }catch (Exception e){
            //出错
            e.printStackTrace();
        }finally {
            //执行结果的最终要释放锁
            lock.unlock();
        }
    }
}

2.利用lua脚本实现redis分布式锁

基于redis的连接过程中,传入命令的方法来实现分布式锁

  1. 配置工具类
@Repository
public class RedisLock {

    /** * 解锁脚本,原子操作 lua脚本 */
    private static final String unlockScript =
            "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n"
                    + "then\n"
                    + " return redis.call(\"del\",KEYS[1])\n"
                    + "else\n"
                    + " return 0\n"
                    + "end";
    @Autowired
    private StringRedisTemplate redisTemplate;

    /** * 加锁,有阻塞 * * @param name * @param expire * @param timeout * @return */
    public String lock(String name, long expire, long timeout) {
        long startTime = System.currentTimeMillis();
        String token;
        do {
            //获取加锁
            token = tryLock(name, expire);
            if (token == null) {
                //执行超时 就结束当前运行
                if ((System.currentTimeMillis() - startTime) > (timeout - 50)) {
                    break;
                }
                try {
                    Thread.sleep(50); //try 50 per sec
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    return null;
                }
            }
        } while (token == null);

        return token;
    }

    /** * 加锁,无阻塞 * * @param name * @param expire * @return */
    public String tryLock(String name, long expire) {
        //随机生成一个令牌
        String token = UUID.randomUUID().toString();
        //获取连接redis的工厂
        RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
        //获取redis的连接
        RedisConnection conn = factory.getConnection();
        try {
            Boolean result = conn.set(name.getBytes(Charset.forName("UTF-8")), token.getBytes(Charset.forName("UTF-8")),
                    Expiration.from(expire, TimeUnit.MILLISECONDS), RedisStringCommands.SetOption.SET_IF_ABSENT);
            if (result != null && result){
                return token;
            }
        } finally {
            RedisConnectionUtils.releaseConnection(conn, factory);
        }
        return null;
    }

    /** * 解锁 * * @param name * @param token * @return */
    public boolean unlock(String name, String token) {
        byte[][] keysAndArgs = new byte[2][];
        keysAndArgs[0] = name.getBytes(Charset.forName("UTF-8"));
        keysAndArgs[1] = token.getBytes(Charset.forName("UTF-8"));
        RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
        RedisConnection conn = factory.getConnection();
        try {
            Long result = (Long) conn.scriptingCommands().eval(unlockScript.getBytes(Charset.forName("UTF-8")), ReturnType.INTEGER, 1, keysAndArgs);
            if (result != null && result > 0) {
                return true;
            }
        } finally {
            RedisConnectionUtils.releaseConnection(conn, factory);
        }
        return false;
    }
}
  1. 分布式锁实现案例
@Autowired
    private RedisLock redisLock;
    @Test
    public void demo2(){
        String token = null;
        try{
            token = redisLock.lock("lock_name", 10000, 11000);
            if(token != null) {
                System.out.print("我拿到了锁哦");
                // 执行业务代码
            } else {
                System.out.print("我没有拿到锁唉");
            }
        } finally {
            if(token!=null) {
                redisLock.unlock("lock_name", token);
            }
        }
    }
正文到此结束
本文目录