• Redisson实现分布式锁


    一:分布式作用,为什么使用?

    如果我们一个电商网站,只剩一件商品可卖,此时用户A进来后,下单付款,扣减库存,在用户A付款的这个过程中,恰好用户B进来了,也看到还有一个库存,用户B也开始下单付款,扣减库存,那么这个过程容易出现超卖的问题。

    而使用分布式锁,用户B必须得等到用户A下单付款,扣减库存后,才可以进行下单操作,从而避免超卖问题。

    二:传统Redis实现的分布式锁

    (1)基本锁

    • 原理:利用redis的setnx,如果不存在某个key则设置值,设置成功则表示取得锁成功。

    • 缺点:如果获取锁后的进程在没有执行完就挂了,则锁永远不会释放,产生死锁。

    (2)改进型

    • 改进:在基本锁上setnx后设置expire,保证超时后也能自动释放锁。

    • 缺点:setnx与expire不是一个原子操作,可能执行完setnx,还没来得及执行expire设置过期时间,该进程就挂了。

    (3)增强版

    • 改进:利用Lua脚本,将setnx与expire变成一个原子操作,可解决一部分问题。

    • 缺点:还是锁过期问题。注:2.6.12开始set支持NX、PX

    public Boolean lock(String key, Long waitTime, Long expireTime) {
            String value = UUID.randomUUID().toString().replaceAll("-", "").toLowerCase();
            Boolean flag = setNx(key, value, expireTime, TimeUnit.SECONDS);
    
            if (flag) {  // 尝试获取锁成功返回
                return flag;
            } else { // 获取失败
                // 现在时间
                long newTime = System.currentTimeMillis();
                // 等待过期时间
                long loseTime = newTime + waitTime;
                // 不断尝试获取锁成功返回
                while (System.currentTimeMillis() < loseTime) {
                    Boolean testFlag = setNx(key, value, expireTime, TimeUnit.MILLISECONDS);
                    if (testFlag) {
                        return testFlag;
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
    
                    }
                }
            }
            return false;
        }

    思路:首先尝试获取锁,获取到后,设置失效时间。获取不到,继续等待。

    问题:失效时间1分钟,业务操作执行了2分钟,锁失效后,上一个线程还没处理完,下一个线程便拿锁。

    处理方法:使用Redisson

    三:Redisson分布式锁

    1、首先引入maven

    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson</artifactId>
        <version>3.11.6</version>
    </dependency>
    

    构建Redisson实例

    @Configuration
    public class RedissonConfig {
    	
        @Value("${spring.redis.host}")
        private String host;
    
        @Value("${spring.redis.port}")
        private String port;
    
        @Bean
    	public RedissonClient redissonClient(){
           Config config = new Config();
           config.useSingleServer().setAddress("redis://"+host+":"+port);
           return Redisson.create(config);
    	}
    }
    
    RedissonController 使用
    @RestController
    public class RedissonController {
    
        @Resource
        private RedisUtil redisUtil;
    
        @Resource
        private RedissonClient redissonClient;
    
        @RequestMapping("/redisson")
         public String TestRedisson(){
             Jedis jedis = redisUtil.getJedis();
             RLock lock = redissonClient.getLock("lock");//声明锁
             lock.lock();//上锁
             String value = jedis.get("redisson-key");
             if(StringUtils.isEmpty(value)){
                 value="1";
             }
             jedis.set("redissonKey",(Integer.parseInt(value)+1)+"");
             jedis.close();
             lock.unlock();//解锁
             return "success";
        }
    }

    注:不使用 lock(long leaseTime, TimeUnit unit) 设置过期时间的,因为并不会去检查任务是否执行结束

    如果任务还没执行结束,然后锁的过期时间到了,线程中断,就会出现异常。

    lock()方法不产传递任何参数,会去校验当前任务是否执行结束,如果没有执行结束,那么相应的就会延长锁的过期时间

    @RestController
    public class RedissonController {

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private RedissonClient redissonClient;

    @RequestMapping("/redisson")
    public String TestRedisson(){
    Jedis jedis = redisUtil.getJedis();
    RLock lock = redissonClient.getLock("lock");//声明锁
    lock.lock();//上锁
    String value = jedis.get("redisson-key");
    if(StringUtils.isEmpty(value)){
    value="1";
    }
    jedis.set("redissonKey",(Integer.parseInt(value)+1)+"");
    jedis.close();
    lock.unlock();//解锁
    return "success";
    }
    }
  • 相关阅读:
    elk 搭建
    Web 开发规范 — WSGI
    Web 开发规范 — WSGI
    fastjson生成和解析json数据,序列化和反序列化数据
    第四章 字典
    Struts2 无后缀action请求
    字典和列表访问方式:
    第3章 使用字符串
    Struts2中的ModelDriven机制及其运用
    Struts2 的Action中取得请求参数值的几种方法
  • 原文地址:https://www.cnblogs.com/fuyublog/p/12060854.html
Copyright © 2020-2023  润新知