分布式锁的两种实现方式

由于项目中遇到,所以今天讲讲分布式锁的两种实现方式。一种利用数据库锁实现,一种利用redis的单线程机制实现。
一个项目中可以同时实现两种方式。在配置文件控制使用哪一种方式,方便当一种方式出现异常时切换到另外一种方式。

分布式锁使用场景:
        所谓分布式锁,是分布式系统或者集群的时候使用的锁,来锁定共享的资源,例如项目的进度,没一笔订单都是修改同一个项目进度,所以项目进度必须加上锁。
        如果只有一台服务器,则不需要使用分布式锁来锁定共享的资源。单台服务器时,可以使用java中的程序锁来锁定修改项目进度的方法,控制每个时刻只有一条线程调用这个方法。
        如果服务器不止一台,即不止一个应用去处理用户的请求,则两台应用可以同时去处理两个用户的请求,此时两条线程可以同时修改项目进度。java的程序锁在这种情况下不能起作用。需要用分布式锁来解决。即 多个线程可以同时操作共享的资源时可以使用分布式锁来解决并发问题。

实现思路:
两种思路,分别对应两个解决方案。
1、利用数据库的锁:
①建立 锁表 tb_lock(id,lock_class(加锁的类),class_lock_id(锁住的那行数据的唯一标识))
②利用数据库select for update 语句获取tb_lock的锁(对于锁住的行加排他锁)
2、利用redis的单线程机制来实现一个锁。由于redis是单线程工作的,所以它存取key-value 的时候是单线程工作的,并且多个线程请求redis并不存在竞争问题。所以可以设置一个标识来作为一把锁,只有获取了该锁之后,才能对共享的资源进行操作,没有拿到锁的线程处于不断去取锁的状态,直到等到上一个线程释放锁(即后一个线程可以取到锁)或者超过规定超时时间不再取锁。
实现:
//调用锁的地方;根据配置文件开关选择使用sql锁还是redis锁
if(SysConfUtil.getConfMsg("lockType").equals("0")){//sql锁
         //参数financingId用户锁定具体的某个项目(为公共资源)
         receiveMap=orderService.sqlLock(financingId);
 }else if(SysConfUtil.getConfMsg("lockType").equals("1")){//redis锁
         receiveMap=orderService.redisLock( financingId);
}
//sqlLockOrderPaySucess实现
    @Transactional  //(必须使用事务)
    public Map<String, Object>  sqlLock(String financingId) throws Exception{
        Map<String, Object> returnData = new HashMap<String, Object>();
        ISqlLockHandler sqlLock = (ISqlLockHandler)SpringUtil.getObject("sqlLockHandler");
        Boolean boo = sqlLock.tryLock("sqlLockFlag",financingId );
        if(boo){//拿到锁再执行业务
            returnData=this.xxx();//业务方法
        }else{
            logger.info("sqlLock获取sql分布式锁失败");
            returnData.put("flag", true);
            returnData.put("msg", "获取sql分布式锁失败");
            throw new RuntimeException("获取sql分布式锁失败");
        } 
        return returnData;
    }

//redisLockOrderPaySucess实现
@Transactional(propagation=Propagation.NOT_SUPPORTED)  //(不能使用事务,否则很可能出现Spring事务超时)
    public Map<String, Object>  redisLock(String financingId)  throws Exception{
        Map<String, Object> returnData = new HashMap<String, Object>();
        // 获取redis 连接对象
        IbatisRedisClient ibatisRedisClient = (IbatisRedisClient) SpringUtil.getObject("ibatisRedisCli_persistence");
        // 获取分布式锁对象
        IRedisLockHandler lockHandler = new RedisLockHandler(ibatisRedisClient);
        // 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false
        if (lockHandler.tryLock("LOCK_"+"sqlLockFlag" + "_"+financingId, 60, TimeUnit.SECONDS))
        {
            try
            {
                returnData=this.xxx();//业务方法
            }
            catch (Exception e)
            {
                throw e;
            }
            finally
            {
                if(lockHandler!=null){
                    //释放锁
                    lockHandler.unLock("LOCK_"+"sqlLockFlag" + "_"+financingId);
                }
            }
        }
        else
        {
            logger.info("redisLock获取redis分布式锁失败");
            returnData.put("flag", true);
            returnData.put("msg", "redisLock获取redis分布式锁失败");
            throw new RuntimeException("redisLock获取redis分布式锁失败");
        }    
        return returnData;
    }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值