美文网首页
从一段代码说起锁和事务

从一段代码说起锁和事务

作者: 15d843cd48a8 | 来源:发表于2020-11-14 20:26 被阅读0次

锁是为了解决高并发产生的多线程对共享资源进行并发访问时,由于后端接口『来不及』处理线程请求的数据,导致最终出现数据不一致或并非预想的结果,比如常见的『抢购商品超卖』、『手机号重复注册』等等。

以这个方法为例展示抢购逻辑:

    @Transactional(rollbackFor = Exception.class)
    public void robWithZKLock(BookRobDto dto) throws Exception{
        //创建ZooKeeper互斥锁组件实例,需要将CuratorFramework实例、精心构造的共享资源 作为构造参数
        InterProcessMutex mutex=new InterProcessMutex(client,pathPrefix+dto.getBookNo()+dto.getUserId()+"-lock");
        try {
            //采用互斥锁组件尝试获取分布式锁-其中尝试的最大时间在这里设置为15s
            if (mutex.acquire(15L, TimeUnit.SECONDS)){
                //真正的核心处理逻辑

                //根据书籍编号查询记录
                BookStock stock=bookStockMapper.selectByBookNo(dto.getBookNo());
                //统计每个用户每本书的抢购数量
                int total=bookRobMapper.countByBookNoUserId(dto.getUserId(),dto.getBookNo());

                //商品记录存在、库存充足,而且用户还没抢购过本书,则代表当前用户可以抢购
                if (stock!=null && stock.getStock()>0 && total<=0){
                    log.info("---处理书籍抢购逻辑-加ZooKeeper分布式锁---,当前信息:{} ",dto);

                    //当前用户抢购到书籍,库存减一
                    int res=bookStockMapper.updateStock(dto.getBookNo());
                    //更新库存成功后,需要添加抢购记录
                    if (res>0){
                        //创建书籍抢购记录实体信息
                        BookRob entity=new BookRob();
                        //将提交的用户抢购请求实体信息中对应的字段取值
                        //复制到新创建的书籍抢购记录实体的相应字段中
                        entity.setUserId(dto.getUserId());
                        entity.setBookNo(dto.getBookNo());
                        //设置抢购时间
                        entity.setRobTime(new Date());
                        //插入用户注册信息
                        bookRobMapper.insertSelective(entity);
                    }
                }else {
                    //如果不满足上述的任意一个if条件,则抛出异常
                    throw new Exception("该书籍库存不足!");
                }

            }else{
                throw new RuntimeException("获取ZooKeeper分布式锁失败!");
            }
        }catch (Exception e){
            throw e;
        }finally {
            //TODO:不管发生何种情况,在处理完核心业务逻辑之后,需要释放该分布式锁
            mutex.release();
        }
    }

可以看到,mutex 的存在让同一时刻只能有一个线程进入逻辑,解决了超卖的问题。

要是单机的话可以使用 Java 语言层面的并发原语入 sync 解决,但是只能解决单机的问题,多机还是要靠分布式锁。所以,直接使用分布式锁应该是一步到位了。

分布式锁

你可以会问,用 synchronized 这样的 Java 锁不行么?在单机时代,可以,但是现在,不行。因为现在都是多实例服务,synchronized 这样的锁只能保证一个 JVM 上在同一时刻只能有一个线程进入。但是在分布式架构下,资源共享不再是传统的线程共享,而是跨 JVM 进程之间资源的共享了,只能使用 ZK 这样的分布式锁来解决。

事务

还是上面的代码,你会看到 @Transactional 这个 Spring 事务,这也是单机的事务,保证这个方法下的 ACID。

分布式事务

那分布式事务呢?你可以看到,这个方法下都是直接调用数据库,而不是调用别人的微服务,这就可以只使用单机事务。试想一下,如果你要调用存库服务、支付服务,而不是调用数据库,这时候就只能使用分布式事务了。

相关文章

  • 从一段代码说起锁和事务

    锁 锁是为了解决高并发产生的多线程对共享资源进行并发访问时,由于后端接口『来不及』处理线程请求的数据,导致最终出现...

  • String源码分析

    从一段代码说起: public void stringTest(){ String a = "a"+"b"+1...

  • Vue.js基础-01-Vue实例和Vue表达式(实例化,常用选

    1. 从一段代码说起 1.1 第一段代码 代码 效果 1.2 Vue的使用说明 从示例中我们可以看到,Vue的使用...

  • 数据库-隔离性的实验

    代码review的时候看到同事把业务锁(insertDelete实现)和业务操作放在一个事务里。 代码结构如下: ...

  • 四 hugegraph源代码- 锁和事务

    上一章说了事务的问题,下面就必然要看看如何实现锁机制的。 ThreadLocal 实现事务管理 还是从 addVe...

  • 事务和锁

    一. 事务 1.1 定义 百度百科事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单...

  • SAP MM 初阶之不常用事务代码MEBV

    SAP MM 初阶之不常用事务代码MEBV 今天从一个同行那里听到事务代码MEBV(extend agreemen...

  • String源码分析

    String源码分析 从一段代码说起: 大家猜一猜结果如何?如果你的结论是true。好吧,再来一段代码: 结果如何...

  • mysql的锁和事务隔离级别

    一 锁的分类以及实现 1.1 锁概念 共享锁:共享锁加上了,只能读不能写,事务a和事务b都可以读数据,但是如果事务...

  • 三级封锁协议

    锁的类型:基本的封锁类型有两种:排它锁(X锁)和共享锁(S锁)X锁,是事务T对数据A加上X锁时,只允许事务T读取和...

网友评论

      本文标题:从一段代码说起锁和事务

      本文链接:https://www.haomeiwen.com/subject/oqgwbktx.html