前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >受限访问量问题中锁的使用

受限访问量问题中锁的使用

作者头像
加多
发布2018-09-06 14:57:11
5320
发布2018-09-06 14:57:11
举报
文章被收录于专栏:Java编程技术Java编程技术

一、 前言

最近在做网上法庭的一个比较有意思的小需求,就是通过扫二维码方式允许最多30个人同时进入庭审,但是不限制进入的是是不是庭审人员,也就是说只要扫了这个二维码并且当前案件对应的参与人数不到30那么就可以进入,始终维持一个庭审案件里面最多有30人。

二、 方案研究

扫描二维码会调用后台的一个rpc,而Rpc会调用bo方法进行处理,那么下面就研究下bo里面里面怎么做。

由于需求是要控制一个庭审的人数,而扫码人肯定是并发的访问这个bo方法,首先会有两种思路使用数据库的锁或者在业务层面进行控制。

2.1 使用乐观锁来控制

case_id

count

1

0

如表每条记录case_id唯一,并且对应一个count字段用来维持进入庭审人员个数。 bo方法都有事务切面的。使用单个数据库。

  • 进入庭审
代码语言:javascript
复制
 prviate final int COUNT = 30
public boolean boEnterMethod(String enCaseid){
    
    第一步解密enCaseid获取真正caseId;
    第二步根据caseId获取记录(里面包含count)nowCount = count ;
        if( nowCount == COUNT){
             return false;
        }
  
    update 表 set count=nowCount+1 where count =nowCount and id = #id;
    Long rows = 更新语句返回行数;
    if(rows == 1){
        处理业务
              return true;
    }
        return false;
}
  • 退出庭审
代码语言:javascript
复制
public boolean boExitMethod(String enCaseid){
    
    第一步解密enCaseid获取真正caseId;
    第二步根据caseId获取记录(里面包含count)nowCount = count;
        if( nowCount == 0){
             return false;
        }
  
    update 表 set count=nowCount-1 where count =nowCount and id = #id;
    Long rows = 更新语句返回行数;
    if(rows == 1){
        处理业务
        return true;

    }
        return false
}

这种方式好处是在执行时候才进行校验不需要提前对记录进行加锁,坏处是,假如两个人同时扫描二维码,获取的nowCount=1那么只有一个能真正进入庭审,另外一个会失败,结果是他进入不了庭审。

乐观锁下有咩有办法解决那?答案是肯定的,还记得AQS里面的trylock?第一次cas失败,那好吧,我再循环一次再试试。所以改进在于可以加个循环,如下:

  • 进入庭审
代码语言:javascript
复制
public boolean boEnterMethod(String enCaseid){
    
  第一步解密enCaseid获取真正caseId;
  for(;;){
        第二步根据caseId获取记录(里面包含count)nowCount = count;
        if( nowCount == COUNT){
             return false;
        }
      
        update 表 set count=nowCount+1 where count =nowCount and id = #id;
        Long rows = 更新语句返回行数;
        if(rows == 1){
            处理业务

            return true;

        }
   }
}
  • 退出庭审
代码语言:javascript
复制
public boolean boExitMethod(String enCaseid){
    
    第一步解密enCaseid获取真正caseId;
    for(;;){
        第二步根据caseId获取记录(里面包含count)nowCount = count;
            if( nowCount == 0){
                 return false;
            }
      
        update 表 set count=nowCount-1 where count =nowCount and id = #id;
        Long rows = 更新语句返回行数;
        if(rows == 1){
            处理业务
            return true;
        }
    }
        
}

加个循环目前是为了避免当访问量不足30时候由于乐观锁竞争导致的失败,这里当当前访问量为30的时候直接返回是为了避免大量请求线程空轮造成tomcat线程池满。但是问题是可能查询数据库的频率比较高。

2.2 使用悲观锁来控制

  • 乐观锁
代码语言:javascript
复制
public boolean boEnterMethod(String enCaseid){
    
    第一步解密enCaseid获取真正caseId;
    第二步根据caseId获取记录(里面包含count)使用select * from 表 where .. for update 对本记录加锁
    nowCount = count;
    rowId = id;
    if( nowCount == COUNT){
         return false;
    }
  
    update 表 set count=nowCount+1 where  id = rowId;
    Long rows = 更新语句返回行数;
    if(rows == 1){
        处理业务
        return true;

    }
           return false;
}
  • 退出庭审
代码语言:javascript
复制
public boolean boExitMethod(String enCaseid){
    
    第二步根据caseId获取记录(里面包含count)使用select * from 表 where .. for update 对本记录加锁
    nowCount = count;
    rowId = id;
    if( nowCount == 0){
         return false;
    }
  
    update 表 set count=nowCount-1 where id = rowId;
    Long rows = 更新语句返回行数;
    if(rows == 1){
        处理业务
        return true;

    }
        return false
}

使用悲观锁方式是事先对记录加锁,其他事务访问时候需要等待,直到当前事务提交。

2.3 使用业务锁来控制

代码语言:javascript
复制
public class TestLock {

    private final int COUNT_NUM = 30;
    private final SafeIntegerCount count = new SafeIntegerCount(COUNT_NUM);

    private final static ConcurrentHashMap<String, SafeIntegerCount> caseLockMap = new ConcurrentHashMap<>();

    //初始化缓存
    public void init() {

        //select所有caseid到list
        for (String caseId:list) {
            SafeIntegerCount count = new SafeIntegerCount(COUNT_NUM);
            caseLockMap.put(caseId, count);
            
        }
    }

    public void boEnterMethod(String enCaseid) {

        对enCaseid进行解密得到caseId
        
        SafeIntegerCount count = caseLockMap.get(caseId);

        // 进入
        if (null == count) {
            return;
        }
        
        try {
            if (count.inc()) {
                // 处理业务
            }
        } catch (Exception e) {
            count.desc();
        }

    }

    public void boExitMethod(String enCaseid) {

        对enCaseid进行解密得到caseId
        
        SafeIntegerCount count = caseLockMap.get(caseId);

        // 进入
        if (null == count) {
            return;
        }
        
        try {
            if (count.desc()) {
                // 处理业务
            }
        } catch (Exception e) {
            count.inc();
        }

    }

}
代码语言:javascript
复制
public class SafeIntegerCount {

    //当前计数
    private int count = 0;

    //最大计数
    private int maxCount = 0;

    //公平独占锁
    private final ReentrantLock lock = new ReentrantLock(true);

    //构造函数设置最大值
    public SafeIntegerCount(int maxCount) {
        this.maxCount = maxCount;
    }

    //自增加一
    public Boolean inc() {

        lock.lock();
        try {
            if (count == maxCount) {
                return false;
            }
            ++count;

            return true;
        } finally {
            lock.unlock();
        }
    }

    //自减-
    public Boolean desc() {

        lock.lock();
        try {
            if(count == 0 ){
                return false;
            }
            --count;
            
            return true;
            
        } finally {
            lock.unlock();
        }
    }

}

使用ReentrantLock实现了一个可以判断上下限的计数器。眨眼看可以解决问题,但是仅仅单台机器可以正常,多台机器下会有问题,另外案件量特别大时候缓存可能占用大量内存。

2.4 总结

推荐使用悲观锁方式。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017.06.09 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、 前言
  • 二、 方案研究
    • 2.1 使用乐观锁来控制
      • 2.2 使用悲观锁来控制
        • 2.3 使用业务锁来控制
          • 2.4 总结
          相关产品与服务
          数据库
          云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档