记录一个Lock和sychronized应用及双检锁

记录一个Lock和sychronized应用及双检锁,第1张

记录一个Lock和sychronized应用及双检锁

synchronied

场景:数据存入redis,并读取缓存。app用户读取,量较大

逻辑

1.先读取缓存,判断缓存中是否有值,有返回,无,继续执行

2.对当前缓存类(CacheTemplateService)对象(this)加锁

3.再次从缓存中获取数据,再次判断,有数据返回,无,继续执行

为什么这里再次判断缓存中是否有锁,加入不判断,如果有大量请求等待持有锁(在synchronized处等待),当持有锁线程执行结束后(已写入缓存),就会有另外一个线程进入同步代码块,又会去查询数据库,写入缓存,其实是多余的,加了再次加缓存判断,即可避免。

4.执行查询数据库逻辑

5.写缓存

工具类

@Slf4j
@Component
public class CacheTemplateService {

    @Autowired
    private RedisManager redisManager;

    public  T fetchCache(String key, Integer expire, TypeReference clazz, CacheLoadable cacheLoadable) {
        String json = (String) redisManager.get(key);
        if (Objects.nonNull(json) && StringUtils.isNotBlank(json) && !json.equalsIgnoreCase("null")
                && !"[]".equals(json) && !"{}".equals(json)) {
            return JSON.parseObject(json, clazz);
        }
        synchronized (this) {
            json = (String) redisManager.get(key);
            if (Objects.nonNull(json) && StringUtils.isNotBlank(json) && !json.equalsIgnoreCase("null")
                    && !"[]".equals(json) && !"{}".equals(json)) {
                return JSON.parseObject(json, clazz);
            }
            // 核心业务
            T result = cacheLoadable.load();
            redisManager.put(key, expire, JSON.toJSonString(result));
            return result;
        }
    }

    public void invalidate(String key) {
        redisManager.remove(key);
    }

}

调用代码

List resLiveVOS = cacheTemplateService
			.fetchCache(RedisEnum.ADVANCE_LIVE_KEY.key, RedisEnum.ADVANCE_LIVE_KEY.expired,
					new TypeReference>() {
					}, new CacheLoadable>() {
					    @Override
					    public List load() {
						return selectAdvanceLive(uid);
					    }
					});

Lock

场景:类似第一种场景,也是先从缓存查询数据,没有会调用第三方接口数据,app用户读取,量较大

逻辑

1.读缓存,无,继续

2.加lock锁

3.再次查询缓存 double check

4.调第三方接口,查询数据

5.写入缓存

    @Override
    public FundCompositeDTO fundCompositeData(String code) {
	if (StringUtils.isBlank(code)) {
	    return null;
	}
	String key = RedisKeyHelper.compositeKey(code);
	FundCompositeDTO cacheData = (FundCompositeDTO) redis.get(key);
	if (cacheData != null) {
	    return cacheData;
	}
	try {
	    lock.tryLock(LOCK_TIMEOUT, TimeUnit.SECONDS);
	    cacheData = (FundCompositeDTO) redis.get(key);
	    if (cacheData != null) {
		return cacheData;
	    }
	    FundCompositeDTO dto = fetchFundCompositeDataFromDB(code);
	    if (dto != null) {
		int expire = (int) DateTimeUtils.getCurrent2EndDiffSenconds();
		float lastScore = getsetLastTotalScore(code, dto.getTotalScore());
		dto.setFlag(dto.getTotalScore() > lastScore);
		redis.put(key, expire, dto);
	    }

	    return dto;
	} catch (Exception e) {
	    log.info("Try Lock failed for the code#{}", code, e);
	    return fetchFundCompositeDataFromDB(code);
	} finally {
	    lock.unlock();
	}
    }

比较二者

执行逻辑基本一样

Lock比较好吗,

lock获取锁有个时间限制(多久,与加锁内容执行时间有关),其他线程等待超时就放弃获取锁。

synchronized呢,经过锁升级过程,效率有所提升。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/zaji/5685490.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存