zoukankan      html  css  js  c++  java
  • 2020最新的Spring Boot 分布式锁的具体实现(内附代码)

    前言

    面试总是会被问到有没有用过分布式锁、redis 锁,大部分读者平时很少接触到,所以只能很无奈的回答 “没有”。本文通过 Spring Boot 整合 redisson 来实现分布式锁,并结合 demo 测试结果。

    首先看下大佬总结的图

    正文

    添加依赖

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

    配置信息

    spring:
    # redis
      redis:
        host: 
    47.103
    .
    5.190
        port: 
    6379
        jedis:
          pool:
    # 连接池最大连接数(使用负值表示没有限制)
            max-active: 100
    # 连接池中的最小空闲连接
            max-idle: 10
    # 连接池最大阻塞等待时间(使用负值表示没有限制)
            max-wait: -1
    # 连接超时时间(毫秒)
          timeout: 5000
    #默认是索引为0的数据库
          database: 0
    

    配置类

    /**
     * redisson 配置,下面是单节点配置:
     *
     * @author gourd
     */
    @Configuration
    publicclass
    RedissonConfig
    {
    @Value
    (
    "${spring.redis.host}"
    )
    private
    String
     host;
    @Value
    (
    "${spring.redis.port}"
    )
    private
    String
     port;
    @Value
    (
    "${spring.redis.password:}"
    )
    private
    String
     password;
    
    @Bean
    public
    RedissonClient
     redissonClient() {
    Config
     config = 
    new
    Config
    ();
    //单节点
            config.useSingleServer().setAddress(
    "redis://"
    + host + 
    ":"
    + port);
    if
    (
    StringUtils
    .isEmpty(password)) {
                config.useSingleServer().setPassword(
    null
    );
    } 
    else
    {
                config.useSingleServer().setPassword(password);
    }
    //添加主从配置
    // config.useMasterSlaveServers().setMasterAddress("").setPassword("").addSlaveAddress(new String[]{"",""});
    // 集群模式配置 setScanInterval()扫描间隔时间,单位是毫秒, //可以用"rediss://"来启用SSL连接
    // config.useClusterServers().setScanInterval(2000).addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001").addNodeAddress("redis://127.0.0.1:7002");
    return
    Redisson
    .create(config);
    }
    }      
    

    Redisson 工具类

    /**
     * redis分布式锁帮助类
     *
     * @author gourd
     *
     */
    publicclass
    RedisLockUtil
    {
    
    privatestatic
    DistributedLocker
     distributedLocker = 
    SpringContextHolder
    .getBean(
    "distributedLocker"
    ,
    DistributedLocker
    .
    class
    );
    
    /**
         * 加锁
         * @param lockKey
         * @return
         */
    publicstatic
    RLock
    lock
    (
    String
     lockKey) {
    return
     distributedLocker.
    lock
    (lockKey);
    }
    
    /**
         * 释放锁
         * @param lockKey
         */
    publicstaticvoid
     unlock(
    String
     lockKey) {
            distributedLocker.unlock(lockKey);
    }
    
    /**
         * 释放锁
         * @param lock
         */
    publicstaticvoid
     unlock(
    RLock
    lock
    ) {
            distributedLocker.unlock(
    lock
    );
    }
    
    /**
         * 带超时的锁
         * @param lockKey
         * @param timeout 超时时间   单位:秒
         */
    publicstatic
    RLock
    lock
    (
    String
     lockKey, 
    int
     timeout) {
    return
     distributedLocker.
    lock
    (lockKey, timeout);
    }
    
    /**
         * 带超时的锁
         * @param lockKey
         * @param unit 时间单位
         * @param timeout 超时时间
         */
    publicstatic
    RLock
    lock
    (
    String
     lockKey, 
    int
     timeout,
    TimeUnit
     unit ) {
    return
     distributedLocker.
    lock
    (lockKey, unit, timeout);
    }
    
    /**
         * 尝试获取锁
         * @param lockKey
         * @param waitTime 最多等待时间
         * @param leaseTime 上锁后自动释放锁时间
         * @return
         */
    publicstaticboolean
     tryLock(
    String
     lockKey, 
    int
     waitTime, 
    int
     leaseTime) {
    return
     distributedLocker.tryLock(lockKey, 
    TimeUnit
    .SECONDS, waitTime, leaseTime);
    }
    
    /**
         * 尝试获取锁
         * @param lockKey
         * @param unit 时间单位
         * @param waitTime 最多等待时间
         * @param leaseTime 上锁后自动释放锁时间
         * @return
         */
    publicstaticboolean
     tryLock(
    String
     lockKey, 
    TimeUnit
     unit, 
    int
     waitTime, 
    int
     leaseTime) {
    return
     distributedLocker.tryLock(lockKey, unit, waitTime, leaseTime);
    }
    
    /**
         * 获取计数器
         *
         * @param name
         * @return
         */
    publicstatic
    RCountDownLatch
     getCountDownLatch(
    String
     name){
    return
     distributedLocker.getCountDownLatch(name);
    }
    
    /**
         * 获取信号量
         *
         * @param name
         * @return
         */
    publicstatic
    RSemaphore
     getSemaphore(
    String
     name){
    return
     distributedLocker.getSemaphore(name);
    }
    }
    

    底层封装

    /**
     * @author gourd
     */
    publicinterface
    DistributedLocker
    {
    
    RLock
    lock
    (
    String
     lockKey);
    
    RLock
    lock
    (
    String
     lockKey, 
    int
     timeout);
    
    RLock
    lock
    (
    String
     lockKey, 
    TimeUnit
     unit, 
    int
     timeout);
    
    boolean
     tryLock(
    String
     lockKey, 
    TimeUnit
     unit, 
    int
     waitTime, 
    int
     leaseTime);
    
    void
     unlock(
    String
     lockKey);
    
    void
     unlock(
    RLock
    lock
    );
    }
    
    /**
     * @author gourd
     */
    @Component
    publicclass
    RedisDistributedLocker
    implements
    DistributedLocker
    {
    
    @Autowired
    private
    RedissonClient
     redissonClient;
    
    @Override
    public
    RLock
    lock
    (
    String
     lockKey) {
    RLock
    lock
    = redissonClient.getLock(lockKey);
    lock
    .
    lock
    ();
    returnlock
    ;
    }
    
    @Override
    public
    RLock
    lock
    (
    String
     lockKey, 
    int
     leaseTime) {
    RLock
    lock
    = redissonClient.getLock(lockKey);
    lock
    .
    lock
    (leaseTime, 
    TimeUnit
    .SECONDS);
    returnlock
    ;
    }
    
    @Override
    public
    RLock
    lock
    (
    String
     lockKey, 
    TimeUnit
     unit ,
    int
     timeout) {
    RLock
    lock
    = redissonClient.getLock(lockKey);
    lock
    .
    lock
    (timeout, unit);
    returnlock
    ;
    }
    
    @Override
    publicboolean
     tryLock(
    String
     lockKey, 
    TimeUnit
     unit, 
    int
     waitTime, 
    int
     leaseTime) {
    RLock
    lock
    = redissonClient.getLock(lockKey);
    try
    {
    returnlock
    .tryLock(waitTime, leaseTime, unit);
    } 
    catch
    (
    InterruptedException
     e) {
    returnfalse
    ;
    }
    }
    
    @Override
    publicvoid
     unlock(
    String
     lockKey) {
    RLock
    lock
    = redissonClient.getLock(lockKey);
    lock
    .unlock();
    }
    
    @Override
    publicvoid
     unlock(
    RLock
    lock
    ) {
    lock
    .unlock();
    }
    }
    

    测试

    模拟并发测试

    /**
     * redis分布式锁控制器
     * @author gourd
     * @since 2019-07-30
     */
    @RestController
    @Api
    (tags = 
    "redisson"
    , description = 
    "redis分布式锁控制器"
    )
    @RequestMapping
    (
    "/redisson"
    )
    @Slf4j
    publicclass
    RedissonLockController
    {
    
    /**
         * 锁测试共享变量
         */
    private
    Integer
     lockCount = 
    10
    ;
    
    /**
         * 无锁测试共享变量
         */
    private
    Integer
     count = 
    10
    ;
    
    /**
         * 模拟线程数
         */
    privatestaticint
     threadNum = 
    10
    ;
    
    /**
         * 模拟并发测试加锁和不加锁
         * @return
         */
    @GetMapping
    (
    "/test"
    )
    @ApiOperation
    (value = 
    "模拟并发测试加锁和不加锁"
    )
    publicvoidlock
    (){
    // 计数器
    final
    CountDownLatch
     countDownLatch = 
    new
    CountDownLatch
    (
    1
    );
    for
    (
    int
     i = 
    0
    ; i < threadNum; i ++) {
    MyRunnable
     myRunnable = 
    new
    MyRunnable
    (countDownLatch);
    Thread
     myThread = 
    new
    Thread
    (myRunnable);
                myThread.start();
    }
    // 释放所有线程
            countDownLatch.countDown();
    }
    
    /**
         * 加锁测试
         */
    privatevoid
     testLockCount() {
    String
     lockKey = 
    "lock-test"
    ;
    try
    {
    // 加锁,设置超时时间2s
    RedisLockUtil
    .
    lock
    (lockKey,
    2
    , 
    TimeUnit
    .SECONDS);
                lockCount--;
                log.info(
    "lockCount值:"
    +lockCount);
    }
    catch
    (
    Exception
     e){
                log.error(e.getMessage(),e);
    }
    finally
    {
    // 释放锁
    RedisLockUtil
    .unlock(lockKey);
    }
    }
    
    /**
         * 无锁测试
         */
    privatevoid
     testCount() {
            count--;
            log.info(
    "count值:"
    +count);
    }
    
    
    publicclass
    MyRunnable
    implements
    Runnable
    {
    /**
             * 计数器
             */
    final
    CountDownLatch
     countDownLatch;
    
    public
    MyRunnable
    (
    CountDownLatch
     countDownLatch) {
    this
    .countDownLatch = countDownLatch;
    }
    
    @Override
    publicvoid
     run() {
    try
    {
    // 阻塞当前线程,直到计时器的值为0
                    countDownLatch.
    await
    ();
    } 
    catch
    (
    InterruptedException
     e) {
                    log.error(e.getMessage(),e);
    }
    // 无锁操作
                testCount();
    // 加锁操作
                testLockCount();
    }
    
    }
    
    }
    

    调用接口后打印值:

    测试结果

    根据打印结果可以明显看到,未加锁的 count-- 后值是乱序的,而加锁后的结果和我们预期的一样。

    由于条件问题没办法测试分布式的并发。只能模拟单服务的这种并发,但是原理是一样,希望对大家有帮助。如有错误之处,欢迎指正。

    最后

    私信回复 资料 领取一线大厂Java面试题总结+各知识点学习思维导+一份300页pdf文档的Java核心知识点总结!

    这些资料的内容都是面试时面试官必问的知识点,篇章包括了很多知识点,其中包括了有基础知识、Java集合、JVM、多线程并发、spring原理、微服务、Netty 与RPC 、Kafka、日记、设计模式、Java算法、数据库、Zookeeper、分布式缓存、数据结构等等。

    file

  • 相关阅读:
    jquery获取transform里面的值
    H5左滑删除JS插件
    IOS微信浏览器点击事件不起作用问题
    jquery获取不了ajax动态添加的内容的解决办法
    H5跟ios、android交互跟数据对接
    JS图片赖加载例子
    苹果手机浏览器下拉会闪动的解决办法
    jquery.rotate.js可选抽奖次数和中奖内容的转盘抽奖demo
    Html5的localStorage与sessionStorage五种循序渐进的使用方法
    tar 「解/壓」 包
  • 原文地址:https://www.cnblogs.com/MonsterJ/p/13262758.html
Copyright © 2011-2022 走看看