荒腔走板聊生活
大家好,一周的时间过的飞快,转眼间又到周末了。
老规矩,还是本号特色,先是荒腔走板的聊聊生活。
上面的图片是我在一次跑步的过程中拍的,一只狗子。可以看到图片中还有一个轨迹图,也是一只狗子。
这个轨迹图全长21km,刚好是一个半马的距离,而且一路上会穿过北海、什刹海、南锣鼓巷、雍和宫、地坛、鼓楼大街、德胜门这些比较知名的景点。我个人非常喜欢这个路线,在北京的时候跑过好几次。
如果你在北京,也有去跑一跑这个轨迹然后发朋友圈装逼的想法,可以在关注公众号后,在后台回复“狗子跑”。我会把具体的路线图回复给你。
最近由于疫情,距离上次跑步已经过了很久很久了,虽然公司有跑步机,但是我一向不太习惯在跑步机上跑。
这周成都基本上解禁了,小区里面跑步的人也慢慢出来了,所以我决定这篇文章写完后,一定要出去跑个至少10km,准备开启这个夏天,毕竟一到夏天,我不经意间漏出的腹肌也该出来活动活动了。
好了,说回文章。
面试后的复盘非常重要
之前写了《求锤得锤之神仙打架》这篇文章,在长发哥出锤这一小节中,里面有写到这样一段话:
于是就有读者来问了:老哥,看门狗介绍一下呗。面试的时候被问到了,没有回答上来。
听到这个问题我脑海里首先浮现出了几个问题:
1.你面试被问到,没有答上来,然后呢?
2.面试结束之后你没有进行面试的复盘吗?
3.对于自己没有回答上来的问题,没有去进行探索吗?
甚至你都忘记了当时你的面试题,只是看到我文章的时候,突然想起:哦,这题我之前遇到过,没有解决。
这个方式是不对的,朋友。
一次面试是一场技术的交锋,所以面试之后的复盘非常非常的重要,面试结束后的第一件事情就应该是回顾整个面试过程,看看在整个面试的过程中,哪些地方是自己知道但是没有说清楚的,哪些地方是自己应该知道但是确实不知道,需要去提升的。
然后立刻、马上、当即在手机标签或者随身笔记上记录下复盘后自己的总结出来的关键点。
这些关键点可以是表现的好的地方,但是更多的应该是需要提升的地方。
也许你也在网上看到过这个套路:面试的过程中有几个问题没有回答上来,最后面试官说你先回去等通知吧。于是面试结束后,你对于没有回答上来的问题进行了学习,然后把自己的学习总结发给面试官。面试官一看,哟,这小伙可以啊,学习能力还不错。
然后就真的通知你准备进行下一轮面试吧。
这招我没用过,但是这个套路,传递的思想就是:在自己领域范围内,不懂的问题,遇到了,你得主动去解决。
面试后的复盘,非常的重要。
复盘过程中的想法形成文字,保留下来,非常非常的重要。
形成文字了,还可以分享出去,帮助后来人。
好了,既然读者问了这个问题,我就稍微扩展一下,把我自己知道的都分享一下。
先看示例代码
Redisson 分布式锁可能大多数朋友都用过。先上个代码给大家看看是怎么用的。
看到这几行代码,你先别往下看,你先想一想,和你自己造的轮子比起来有什么非常明显不一样的地方?
我给大家分享一下我第一次用 Redission 做分布式锁的时候遇到的两个非常直观的疑问吧。
1.value去哪里了?
2.过期时间去哪里了?
之前说过,如果是我们自己造轮子,基于 Redis 做分布式锁的话,需要向 Redis 发一条下面的命令:
SET key random_value NX PX 3000
而在我们上面的示例代码中,为什么只有 key 没有 value 呢?
我们知道 value 是必须要有的。还记得《求锤得锤之神仙打架》这篇文章里面说的,当面试官问:
你给我讲一讲基于Redis的加锁和释放锁的细节吧。
我们从三个关键点中去回答:
1.原子命令加锁。
2.设置值的时候,放的是random_value。
3.value 的值设置为随机数主要是为了更安全的释放锁,释放锁的时候需要检查 key 是否存在,且 key 对应的值是否和我指定的值一样,是一样的才能释放锁。所以可以看到这里有获取、判断、删除三个操作,为了保障原子性,我们需要用 lua 脚本。
所以,这个 value 是非常重要的。
另外,第 3 步,释放锁的时候为什么需要 lua 脚本,也有读者问过,其实这事几句话就能说清楚,所以我在这里插播一下:
你看这三个操作:获取、判断、删除。
获取操作,只读不写,没有任何问题。问题就出在判断和删除之间。如果不是原子操作,出现了下面的情况:
1.线程 A 在判断了 value 是自己放进去的,在执行 key 删除操作之前,程序 GC 导致了 STW。
2.STW 期间线程 A 的锁虽然没有执行删除操作,但是由于时间到期被 redis 释放了。
3.STW 之后,在线程 A 执行删除操作之前,线程 B 加了同样 key 的锁。
4.结果你猜怎么着?线程 A 把线程 B 加的锁删除了。这就出问题了。
为什么 lua 脚本可以解决这个问题呢?
因为 lua 脚本的执行是原子性的,再加上 Redis 执行命令是单线程的,所以在 lua 脚本执行完之前,其他的命令都得等着。就不会出现上面说的情况了。
第二个问题是过期时间去哪里了呢?
看上面的加锁代码,像是没有设置过期时间似的。
我们先说说没有过期时间的问题是什么。很明显嘛,容易造成死锁。
加锁操作的服务器,在没有执行释放锁操作之前,服务器崩了。
哦豁,喜提死锁一把。
value去哪了?
对于这个问题,首先我们需要确定的是,value一定是有的。
当我们自己放 value 的时候,一般就是搞个随机值,往里面一塞就完事了。
另外,我见过网上有些分析 Redis 分布式锁的文章里面 value 直接扔个 OK 进去。前面我们说过,这是不对啊,朋友们。要注意辨别。
用 Redssion 时,我们知道这个 key 肯定是框架帮我们生成了。所以我们只需要去源码中验证我们的想法即可
但是,先别慌,我们还有一个更加简单的验证方法:程序跑起来,然后去 Redis 里面看一眼不就完事了?
看了一眼后发现,不错哦,不仅验证了我们的想法,还有意外收获呢。
意外收获一:我们看到了 TTL:25 说明虽然我们没用设置过期时间,但是框架帮我们把过期时间设置好了。这部分在这一小节中先按下不表,等下一小节详细描述。
意外收获二:可以看到我们放进去的 why 是一个 Hash 类型。并不是我们常用的 String 类型。
很明显,key 是 UUID:1,这个 1 是什么含义呢?
为什么要用 Hash 类型,而不用 String 类型呢?
我们带着这两个疑问去看一眼源码。
注意本文中的 Redssion 的 Maven 版本为 3.12.3。
Redssion 的源码非常好 Debug,我建议你自己实际操作一遍。
首先 lock 操作会调用到这个方法:
org.redisson.RedissonLock#lock(long, java.util.concurrent.TimeUnit, boolean)