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

1 天前
 suloser

最近看了一道面试题(先查缓存,缓存中没有再查数据库),其实很久以前就遇到过,我以为已经掌握了,
但是今天再看,分析起来总觉得有些吃力,有种拿着答案分析过程的感觉,没有真正理解。
根据我的经验,应该在大脑中形成一个足够抽象的结构图,下次遇到这类问题,都可以套用。
想问问 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());
} 
1217 次点击
所在节点    问与答
9 条回复
cloudzhou
1 天前
你先理解这道题目的本质
如果只是 get cache -> check -> db -> put cache 那么逻辑非常简单

难度是 synchronized getLock 的使用,是为了解决什么问题呢?
1. 同一个商品 shopId 应该使用同一个锁
2. 如果已经有一个线程在进行补充 cache 的动作了,其他线程等待,do once 的动作

1 、2 结合起来,需要一个 synchronized getLock
同时因为并发原因,需要一个 double check
suloser
1 天前
@cloudzhou #1 你这个分析是对的,其实这段代码是一个引子,我更想知道,大家遇到这类问题时大脑怎么分析?思考过程是什么样?假如换成其他的并发问题如何分析?是否有一套可以复用的思维结构?
SachinBeyond
23 小时 58 分钟前
@cloudzhou 有两个问题我想问下
1 ,需要一个 synchronized getLock 是啥意思? getlock 需要 加 synchronized 关键字?
2 ,楼主代码给了这么多,这个和 shopeCache 直接 computeifabsent 有什么区别?直接 compute ifabsent 不就可以了吗?
MrZhaoyx
22 小时 52 分钟前
@SachinBeyond
1. synchronized 不是对 getLock 加锁,是使用 getLock 获取的对象作为锁,对 synchronized 内部的代码块加锁。
2. 我猜是防止缓存击穿,如果缓存不存在或者过期,同一时间大量请求获取该过期的 shop ,大量请求在同一时间穿透到数据库。
yidinghe
22 小时 31 分钟前
shopCache 本身就可以调用 computeIfAbsent 方法,何必多此一举。
smallest10
20 小时 49 分钟前
确实 shopCache.computeIfAbsent 就解决问题了。

单说怎么写并发安全代码的话,如果手很生么,那就先写正常业务逻辑代码,然后找其中的并发问题点,然后加锁解决并发安全问题。加锁的时候,你就会发现锁附近有些代码有性能优化空间,顺便再优化优化。就完了。
cloudzhou
10 小时 53 分钟前
@SachinBeyond 是可以,这个代码有点像 computeIfAbsent 的实现一样,倒是可以作为一个面试题

@suloser 先理解并发的本质,Java concurrency in practice 这本书就很好
sakasaka
8 小时 57 分钟前
脑海里要能构建出多线程并发的画面
SachinBeyond
48 分钟前
@cloudzhou

这个图可以结题了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1241128

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX