GoForum › 🌐 V2EX

如何分析这段线程安全的代码?

suloser · 2026-09-10 19:08 · 0 次点赞 · 2 条回复

最近看了一道面试题(先查缓存,缓存中没有再查数据库),其实很久以前就遇到过,我以为已经掌握了,
但是今天再看,分析起来总觉得有些吃力,有种拿着答案分析过程的感觉,没有真正理解。
根据我的经验,应该在大脑中形成一个足够抽象的结构图,下次遇到这类问题,都可以套用。
想问问 v 友们如何分析这类问题?思维过程是怎样的?会在大脑中构建图像吗?

private final Map<Long, Shop> shopCache = new ConcurrentHashMap<>();

public Shop getShopById(Long shopId) {
    // 1. 先查本地缓存
    Shop shop = shopCache.get(shopId);
    if (shop != null) {
        return shop;
    }

    // 2. 同一个 shopId 加锁
    synchronized (getLock(shopId)) {

        // 3. 双重检查
        shop = shopCache.get(shopId);
        if (shop != null) {
            return shop;
        }

        // 4. 缓存没有,查数据库
        shop = shopMapper.selectById(shopId);

        // 5. 放入本地缓存
        if (shop != null) {
            shopCache.put(shopId, shop);
        }

        return shop;
    }
}

private final Map<Long, Object> locks = new ConcurrentHashMap<>();

private Object getLock(Long shopId) {
    return locks.computeIfAbsent(shopId, k -> new Object());
} 
2 条回复
cloudzhou · 2026-09-10 19:18
#1

你先理解这道题目的本质 如果只是 get cache -> check -> db -> put cache 那么逻辑非常简单

难度是 synchronized getLock 的使用,是为了解决什么问题呢?

  1. 同一个商品 shopId 应该使用同一个锁
  2. 如果已经有一个线程在进行补充 cache 的动作了,其他线程等待,do once 的动作

1 、2 结合起来,需要一个 synchronized getLock 同时因为并发原因,需要一个 double check

suloser · 2026-09-10 19:48
#2

@cloudzhou #1 你这个分析是对的,其实这段代码是一个引子,我更想知道,大家遇到这类问题时大脑怎么分析?思考过程是什么样?假如换成其他的并发问题如何分析?是否有一套可以复用的思维结构?

添加回复
你还需要 登录 后发表回复

登录后可发帖和回复

登录 注册
主题信息
作者: suloser
发布: 2026-09-10
点赞: 0
回复: 0