JUC(21)

128 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第21天,点击查看活动详情

9.5 应用之缓存

1. 缓存更新策略

更新时,是先清缓存还是先更新数据库

先清缓存

不仅会出现脏读,还可能会导致缓存击穿,并且更新数据库速度较慢,不太可靠

先更新数据库

虽然还是有可能拿到旧值,但是几率小,并且后续读可以读到最新值

如果要严格的保证数据库和缓存的一致性,可以加锁

上面的方法要么牺牲性能换取一致性,要么通过短暂的不一致性换取性能,因此有必要再来一种方法了

2. 读写锁实现一致性缓存

使用读写锁实现一个简单的按需加载缓存

  • 以上实现体现的是读写锁的应用,保证缓存和数据库的一致性,但有下面的问题没有考虑

    • 适合读多写少,如果写操作比较频繁,以上实现性能低
    • 没有考虑缓存容量
    • 没有考虑缓存过期
    • 只适合单机
    • 并发性还是低,目前只会用一把锁
    • 更新方法太过简单粗暴,清空了所有 key(考虑按类型分区或重新设计 key)
  • 乐观锁实现:用 CAS 去更新

9.6 读写锁原理

1.图解流程

1.读写锁用的是同一个 Sycn 同步器,因此等待队列、state 等也是同一个

t1 w.lock,t2 r.lock

分析写锁源码可知,当c也就是state不等于0时线程会判断有没有其他线程加了读锁并且还会判断是否为自身可重入加的写锁,如果都不是就能加写锁成功。当c==0时直接就可以进行写锁添加

1) t1 成功上锁,流程与 ReentrantLock 加锁相比没有特殊之处,不同是写锁状态占了 state 的低 16 位,而读锁 使用的是 state 的高 16 位