为什么java 操作redis缓存,加读写锁

你是想了解为什么在Java中操作Redis缓存时使用读写锁吗?

1,为什么使用读写锁

 

2,实际应用场景

对帐单进行操作时,避免同一个订单被同时操作,操作场景如下

1,保存账单

2,线下结算

代码如下,如果把读写锁的代码注释掉,会存在什么问题?

考虑并发情况,不同场景的并发或同一个场景的并发

比如同一个场景,线下结算,并发操作。

加锁逻辑

lock

// todo

unlock

-----------------------

具体如下

lock

因为存在多个帐单,用set集合存储

先查一下,现在是否有其他人操作,即从缓存中查询。

有,继续判断是否有交集,有,终止

没有,把当前操作的账单号存入redis。代表他正在操作。

unlock

业务逻辑执行完毕,释放锁,从redis set集合中,删除

---------------------------

存在问题分析

流程分析:

如果不加锁,线下结算并发

线程1,单号1,判断是否已在处理,-》否,-〉加入redis set集合-〉执行业务逻辑

线程2,单号1,判断是否已在处理,redis存在,流程结束。

好像没有问题

放大量,推到极致

如果线程1,100条数据,读redis,判断set集合是否为空,为空直接加入,set集合。

不为空,判断新入参100条与set集合已存在的数据,是否包含交集,是,流程结束。否,加入redis set集合,(这里加入redis这个过程比较慢)....

与此同时

线程2,100条(与线程1有交集),同样做上面的判断,读redis(同时线程1还在写redis,交集部分还没有写入),这时候判断线程1中100条与线程2中的100条,虽然有交集,此时是判断不出来的。

这时候问题就出现了

解决方案

线程1在写redis时,线程2是不应该读redis的。写与读应该是互斥的。

怎么控制这个问题,就引入了读写锁。

读写锁怎么解决并发操作的

线程1在写,线程2,不能读,线程2怎么处理的?

排队,线程1释放写锁时。线程2,才能执行读锁逻辑

读写锁控制点

1,加锁时。读时,加读锁,写时,加写锁

2,释放锁时。加写锁,从redis删除元素。

总共加了一个读锁,两个写锁

代码

加锁

 /**
     * 缓存操作中的账单号
     *
     * @param billNoList
     */
    @SuppressWarnings("unchecked")
    public void inOperationBillNoCache(List<String> billNoList) {
        log.info("inOperationBillNoCache--->billNoList:{}", JSON.toJSONString(billNoList));
//        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(Constant.LEASE_BILL_IN_OPERATION_KEY);
//        RLock rLock = readWriteLock.readLock();
//        rLock.lock();
//        try {
            if (redisService.hasKey(Constant.LEASE_BILL_IN_OPERATION_CACHE)) {
                Set<String> inOperationBillNoSet = redisService.smembers(Constant.LEASE_BILL_IN_OPERATION_CACHE);
                log.info("inOperationBillNoCache--->inOperationBillNoSet:{}", JSON.toJSONString(inOperationBillNoSet));
                if (CollectionUtils.isNotEmpty(inOperationBillNoSet)) {
                    List<String> multipleOperationBillNoList = (List<String>) CollectionUtils.intersection(inOperationBillNoSet, billNoList);
                    throw new LeaseServiceException(ErrConstant.INVALID_DATAFILED, String.format("账单[%s]存在多人操作,请刷新后重试", String.join(",", multipleOperationBillNoList)));
                }
            }
//        }
//        } finally {
//            rLock.unlock();
//        }

//        RLock writeLock = readWriteLock.writeLock();
//        writeLock.lock();
//        try {
            billNoList.forEach(billNo -> redisService.sadd(Constant.LEASE_BILL_IN_OPERATION_CACHE, billNo));
//        } finally {
//            writeLock.unlock();
//        }
    }

释放锁 

    /**
     * 删除操作完成的账单号缓存
     *
     * @param billNoList
     */
    public void removeInOperationBillNoCache(List<String> billNoList) {
        log.info("removeInOperationBillNoCache--->billNoList:{}", JSON.toJSONString(billNoList));
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(Constant.LEASE_BILL_IN_OPERATION_KEY);
        RLock writeLock = readWriteLock.writeLock();
        writeLock.lock();
        try {
            billNoList.forEach(billNo -> redisService.srem(Constant.LEASE_BILL_IN_OPERATION_CACHE, billNo));
        } finally {
            writeLock.unlock();
        }
    }

3,思考

上文第二点,分析到并发存在问题。需要加读写锁

读写锁作用

1,提高并发性能

加锁只会降低并发度,不加锁效率应该最高的。读锁等于没有加锁,写锁反而降低了并发度。

这里好像没有体现出,并发性能的提高

2,确认数据一致性

避免了读写冲突

数据隔离:多线程,1线程写时,其他线程无法读写。读时,其他线程不能写,只能读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值