java ReentrantLock 怎么缩写锁的粒度

1、背景

目前是有很多个查询条件,比如版本(v3.90.1,v3.90.1,v3.90.2等),系统(安卓,ios),应用id等,希望按某个查询条件来锁定查询资源,可以使用ReentrantLock 来缩小锁的粒度。

2、使用

2.1 核心代码

    
private Map<String, LocalCacheEntry> map = new ConcurrentHashMap<>();

    /**
     * 使用java本地锁查询数据
     */
    private SourceDataResult getDataByLocalLock(String mediaKey, AppSourceParam param) {
        LocalCacheEntry resultEntry = map.compute(mediaKey, (k, entry) -> {
            if (entry == null) {
                entry = new LocalCacheEntry(mediaKey, param);
            }
            return entry;
        });
        SourceDataResult dataResult = resultEntry.loadData();
        return dataResult;
    }

 /**
     * java锁,限制锁的范围是mediaKey
     */
    public class LocalCacheEntry {

        private final ReentrantLock lock = new ReentrantLock();
        private final String mediaKey;
        private final AppSourceParam param;

        public LocalCacheEntry(String mediaKey, AppSourceParam param) {
            this.mediaKey = mediaKey;
            this.param = param;
        }

        public SourceDataResult loadData() {
            try {
                int count = 0;
                for (; ; ) {
                    count++;
                    if (lock.tryLock() || lock.tryLock(20, TimeUnit.MILLISECONDS)) {
                        SourceDataResult dataResult = mediaSourceCache.getIfPresent(mediaKey);
                        if (dataResult != null) {
                            logger.info("get lock and cache have data,mediaKey:{}", mediaKey);
                            return dataResult;
                        }

                        //查数据库
                        try (Jedis jedis = mediaSourceJedisPool.getResource()) {
                            List<SourceGroupVO> sourceGroups = SourceMediaServiceImpl.this.getDataFromDb(param);
                            SourceDataResult result = sourceListConvertMap(sourceGroups);
                            logger.info("get data from db,mediaKey:{}", mediaKey);
                            jedis.setex(mediaKey, SourceConstants.SOURCE_EXPIRE_TIME, JSONObject.toJSONString(result));
                            mediaSourceCache.put(mediaKey, result);
                            return result;
                        } catch (Exception e) {
                            logger.error("set data to redis error, param:{}", JSONObject.toJSONString(param), e);
                        } finally {
                            logger.info("release lock,mediaKey:{}", mediaKey);
                            lock.unlock();
                        }
                    } else if (SourceMediaServiceImpl.this.getDataResultFromRedis(mediaKey) != null) {
                        logger.info("not get lock but cache have data,mediaKey:{}", mediaKey);
                        break;
                    } else if (count >= 3) {
                        logger.info("the loop did not get data,mediaKey:{}", mediaKey);
                        return new SourceDataResult();
                    }
                }
            } catch (Exception e) {
                logger.error("load data error,mediaKey:{}", mediaKey, e);
            }
            return SourceMediaServiceImpl.this.getDataResultFromRedis(mediaKey);
        }
    }

 2.2 讲解

因为会有很多种查询条件,将每个条件组装成key,作为map的key,value是自定义的对象。将ReentrantLock锁住自定义的对象LocalCacheEntry ,这样能降低锁的粒度。for循环用死循环(自旋),lock.tryLock() || lock.tryLock(20, TimeUnit.MILLISECONDS) 是获取到锁或者等20ms获取到锁就执行查询(使用20ms的目的防止一直尝试获取锁,导致cpu飙高),否则判断redis是否有数据,有了则拿取数据,最后如果循环三次还未获取到数据,则返回空数据给前端。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值