飞道的博客

Redisson实现分布式锁

597人阅读  评论(0)

Redisson简介

Redis 是最流行的 NoSQL 数据库解决方案之一,而 Java 是世界上最流行(注意,没有说“最好”)的编程语言之一。虽然两者看起来很自然地在一起“工作”,但是要知道,Redis 其实并没有对 Java 提供原生支持。

相反,作为 Java 开发人员,我们若想在程序中集成 Redis,必须使用 Redis 的第三方库。而 Redisson 就是用于在 Java 程序中操作 Redis 的库,它使得我们可以在程序中轻松地使用 Redis。Redisson 在 java.util 中常用接口的基础上,为我们提供了一系列具有分布式特性的工具类。

Redisson底层采用的是Netty 框架。支持Redis 2.8以上版本,支持Java1.6+以上版本。

Redisson实现分布式锁步骤

引入依赖

创建spring boot web 项目。引入依赖

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

application.yml

server:
  port: 8090
spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: 

Redisson 配置类

springBoot 启动类中加入redis配置 :

package com.service.redis.servicespringbootredisdemo;

import org.redisson.Redisson;
import org.redisson.config.Config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class ServiceSpringbootRedisDemoApplication {
   

    public static void main(String[] args) {
   
        SpringApplication.run(ServiceSpringbootRedisDemoApplication.class, args);
    }

    @Bean
    public Redisson redisson(){
   
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379").setDatabase(0);
//        config.useCustomServers().;
        return (Redisson) Redisson.create(config);
    }

}

也可以使用redis的配置类:

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
import java.io.IOException;
 
@Configuration
public class RedissonConfig {
   
 
    @Value("${spring.redis.host}")
    private String host;
 
    @Value("${spring.redis.port}")
    private String port;
 
    //@Value("${spring.redis.password}")
    //private String password;
 
    /**
     * RedissonClient,单机模式
     * @return
     * @throws IOException
     */
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redisson() throws IOException {
   
        Config config = new Config();
        //config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password);
        config.useSingleServer().setAddress("redis://" + host + ":" + port);
        return Redisson.create(config);
    }
}

Redisson分布式锁实现

这个是重要的部分,而且业务代码也不多。
redisson 实现还是很简单的

package com.service.redis.servicespringbootredisdemo.test;


import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

@RestController
public class TestController {
   

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

    @Autowired
    private Redisson redisson;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    
    @RequestMapping("deductStock1")
    public String deductStock1() {
   
        String lockKey = "lockKey";
        RLock redissonLock = redisson.getLock(lockKey);
        try {
   

            //加锁,实现锁续命功能
            redissonLock.lock();
            //尝试加锁,最大等待时间300毫秒,上锁30毫秒自动解锁
            //if (redissonLock.tryLock(300,30,TimeUnit.MILLISECONDS)){
   
                //你自己的业务啊,随便写!
                int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
                if (stock > 0) {
   
                    int realStock = stock - 1;
                    stringRedisTemplate.opsForValue().set("stock", realStock + "");
                    System.out.println("扣减库存成功,剩余:" + realStock + "");
                } else {
   
                    System.out.println("扣减失败");
                }
            //}

        } catch (InterruptedException e) {
   
            System.out.println("异常!!!");
            throw new RuntimeException(e);
        } finally {
   
            //解锁
            redissonLock.unlock();

        }


        return "end";
    }
}

tryLock一般用于特定满足需求的场合,但不建议作为一般需求的分布式锁,一般分布式锁建议用void lock(long leaseTime, TimeUnit unit)。因为从性能上考虑,在高并发情况下后者效率是前者的好几倍。
tryLock(long waitTime, long leaseTime, TimeUnit unit)
在源码中出现leaseTime时间判断的有2个分支,实际上就是加锁时是否设置过期时间,未设置过期时间(-1)时则会有watchDog的锁续约,注册了加锁事件的续约任务。

经过多次演变。全部贴上。

package com.service.redis.servicespringbootredisdemo.test;


import org.redisson.Redisson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

@RestController
public class TestController {
   

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

    @Autowired
    private Redisson redisson;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @RequestMapping("deductStock")
    public String deductStock() {
   
        String lockKey = "lockKey";
        String uuid = UUID.randomUUID().toString();
        try {
   
            //下面这行代码相当于jedis.setnx(key,value);
//            Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "test");
            //加超时时间(如果在这里挂掉还不是完蛋)
//            stringRedisTemplate.expire(lockKey,10, TimeUnit.SECONDS);
            //上面两行结合成下一行 ,不管是30还是多少,有的线程执行多,有的少.缺少一个自动续期。

            Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, uuid, 30, TimeUnit.SECONDS);
            if (!result) {
   
                System.out.println("取锁失败,请稍后重试。");
                //这里也可以处理自己的业务。返回错误码什么的、
                return "error";
            }
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
   
                int realStock = stock - 1;
                stringRedisTemplate.opsForValue().set("stock", realStock + "");
                System.out.println("扣减库存成功,剩余:" + realStock + "");
            } else {
   
                System.out.println("扣减失败");
            }
        } finally {
   
            //用完以后肯定是要删掉的
            if (uuid.equals(stringRedisTemplate.opsForValue().get(lockKey)))
                //保证每个线程只删自己的。
                stringRedisTemplate.delete(lockKey);
        }


        return "end";
    }

}


转载:https://blog.csdn.net/csl12919/article/details/129158628
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场