java取map中一model的list值_并发时用Map做缓存(下)

上次的推送,我们讲到了一种提供并发协作的Map缓存,代码如下:

2c9a000e6dfc3c0ce1480da89b0f4cff.png

我们说,以上的实现是几乎完美的它展现了非常好的并发性,能很快地返回已经缓存的结果,如果新到的线程请求的是其他线程正在查询的结果,它会耐心等待

那么,“几乎”指的是什么问题呢?

锵锵

“查询”方法中的if代码块是非原子的,因此就存在着:两个线程几乎在同一时间调用本方法时,双方都没在缓存中取得期望的值,两者又同时开始了数据库的访问工作。如下图所示:

0c19e95e6d9e59400e804d04e724f1e4.png

上图中,偶发的时序导致该方法进行了两次的查询操作。那我们要怎么解决这个问题呢?

锵锵

ConcurrentHashMap类的

putIfAbsent方法

putIfAbsent(K, V)是一个原子方法,如果Map中不含有K则将K-V加入Map并返回null,否则返回Map中已存在的K对应的V。

新的代码如下:

d2d47b6b8be3855361ee220ed04f5be0.png

一切看起来如此美妙!

453ad0150c6b8e5a82e91ea7e84cc3b7.gif

突然有一天,DBMS这个重量级软件突然崩溃了,所有不在缓存中的新题目都要去这个不可能访问到的地方取数据

运维团队很给力,只用了10分钟就解决了数据库的问题,可是随之而来的却是奇怪的Java异常……

原来,在数据库崩溃的10分钟内,评判系统需要找到试卷的第2题。可是,ProblemDaoImpl费了九牛二虎之力也没能成功取到数据库的数据,评判时,每传入“2”并调用“查询”方法时该方法就会抛出一个异常,哪怕是修复了数据库以后……

也就是说,缓存Future对象带来了

缓存污染(cache pollution)

的新问题!

这里的缓存污染指的是,如果一个操作被取消或者它本身失败了,未来尝试对它调用get()时,其永远表现为取消或者失败

为了解决避免这个问题,我们应该实现这样的功能:

  • 每当操作被发现取消时,方法应该负责把Future从缓存中移除;
  • 同时,产生RuntimeException时也应如此,这样新请求中的操作才有可能成功。

修改后的代码如下:

808f16fce50e5ee0ea490a5bee3645ae.png

经过以上的修改,

我们实现了一个

足够健壮的ProblemDaoImpl。

5064e549787f505ab28d1eb7c7ee237c.gif

本文中心思想来自于

《Java并发编程实践(Java Concurrency in Practice)》

be546f5029ea93f9012ae6afe7a42987.png
df340ff362627a0b499010192a2c44ba.gif

附:本文的最终目的是引入备忘录技术(memoization)。

备忘录一种用于缓存计算结果的常见技术,它常常被用于提升递归效率等场景。

下面是在Java中实现的Memoizer。

1b043686ed141757a46f29b61e4e4ccd.png
f6b09a217a36d71990e49cdd9b83eb19.png
fe3ee531e4275d87a3b66eab42db709f.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值