Blog

情境的改变让能量得以更新

Content #

心理学家安妮特拉·卡斯滕(Anitra Karsten)设计实验,来证明了“情境的改变让能量得以更新”。

大约一个世纪之前,心理学家安妮特拉·卡斯滕(Anitra Karsten)邀请人们来完成一些重复性的任务,只要人们愿意就可以一直做,如果累了就停下。在很长的一段时间里,参加者努力完成画图或大声朗诵诗歌的任务,直到他们再也没法应付为止。有一个人的任务是一遍又一遍地写“ababab”。正如哈佛大学的心理学家埃伦·兰格(Ellen Langer)回忆的:“他一直做下去,直到心理上和生理上都精疲力竭了。他的手麻木了,好像哪怕再多写一笔都不行了。这个时候,研究者让他为了另外的目的写下自己的名字和地址,他很容易就做到了。”

同样奇怪的事也发生在其他受试者身上。一位女性说,她太累了,没法举起胳膊再写一笔,但是她接下来就举起胳膊,调整了自己的头发,很显然没有任何困难或不适。当受试者大声朗读诗歌,直到声音嘶哑时,他们可以毫不费力地抱怨这个任务——在他们抱怨的时候,他们的声音听上去就不嘶哑了。据兰格所说,他们并不是在伪装,而是“情境的改变让能量得以更新。”

Viewpoint #

From #

预防死锁的方法

Content #

基本上死锁的发生是因为:

  1. 互斥条件,类似 Java 中 Monitor 都是独占的,要么是我用,要么是你用。
  2. 互斥条件是长期持有的,在使用结束之前,自己不会释放,也不能被其他线程抢占。
  3. 循环依赖关系,两个或者多个个体之间出现了锁的链条环。

所以,我们可以据此分析可能的避免死锁的思路和方法。

  1. 尽量避免使用多个锁,并且只有需要时才持有锁。

嵌套的 synchronized 或者 lock 非常容易出问题。

  1. 如果必须使用多个锁,尽量设计好锁的获取顺序。

一般的情况,我建议可以采取些简单的辅助手段,比如:将对象(方法)和锁之间的关系,用图形化的方式表示分别抽取出来。然后根据对象之间组合、调用的关系对比和组合,考虑可能调用时序。按照可能时序合并,发现可能死锁的场景。

  1. 使用带超时的方法,为程序带来更多可控性。

类似 Object.wait(…) 或者 CountDownLatch.await(…),都支持所谓的 timed_wait,我们完全可以就不假定该锁一定会获得,指定超时时间,并为无法得到锁时准备退出逻辑。

  1. 通过静态代码分析(如 FindBugs)去查找固定的模式,进而定位可能的死锁或者竞争情况。

Viewpoint #

From #

MySQL中的乐观锁与悲观锁

Content #

悲观锁一般就是利用类似 SELECT … FOR UPDATE 这样的语句,对数据加锁,避免其他事务意外修改数据。

乐观锁则与 Java 并发包中的 AtomicFieldUpdater 类似,也是利用 CAS 机制,并不会对数据加锁,而是通过对比数据的时间戳或者版本号,来实现乐观锁需要的版本判断。

Viewpoint #

From #

无情的自私性

Content #

在一个具有高度竞争性的世界上,我们的基因能成功生存下来,其最突出的特性是什么?

我们的基因,有的存续长达几百万年。这使我们有理由在我们的基因中发现某些特性。我将要论证,成功基因的一个突出特性就是其无情的自私性。这种基因的自私性通常会导致个体行为的自私性。然而我们也会看到,基因为了更有效地达到其自私的目的,在某些特殊情况下,也会滋长一种有限的利他主义。

Viewpoint #

From #

自私的基因

延迟双删

延迟双删 #

Redis中的“延迟双删”指的是什么操作?它是用来解决什么问题的?

情况:先删除缓存,再更新数据库。假设线程 A 删除缓存值后,还没有来得及更新数据库(比如说有网络延迟),线程 B 就开始读取数据了,那么这个时候,线程 B 会发现缓存缺失,就只能去数据库读取。这会带来两个问题:

  1. 线程 B 读取到了旧值;
  2. 线程 B 是在缓存缺失的情况下读取的数据库,所以,它还会把旧值写入缓存,这可能会导致其他线程从缓存中读到旧值。

等到线程 B 从数据库读取完数据、更新了缓存后,线程 A 才开始更新数据库,此时,缓存中的数据是旧值,而数据库中的是最新值,两者就不一致了。

在线程 A 更新完数据库值以后,我们可以让它先 sleep 一小段时间,再进行一次缓存删除操作。之所以要加上 sleep 的这段时间,就是为了让线程 B 能够先从数据库读取数据,再把缺失的数据写入缓存,然后,线程 A 再进行删除。所以,线程 A sleep 的时间,就需要大于线程 B 读取数据再写入缓存的时间。这个时间怎么确定呢?建议你在业务程序运行的时候,统计下线程读数据和写缓存的操作时间,以此为基础来进行估算。这样一来,其它线程读取数据时,会发现缓存缺失,所以会从数据库中读取最新值。因为这个方案会在第一次删除缓存值后,延迟一段时间再次进行删除,所以我们也把它叫做“延迟双删”。

Viewpoint #

From #

可视化引用包objgraph

可视化引用包objgraph #

objgraph,一个非常好用的可视化引用关系的包。在这个包中,我主要推荐两个函数,第一个是 show_refs(),它可以生成清晰的引用关系图。

通过下面这段代码和生成的引用调用图,你能非常直观地发现,有两个 list 互相引用,说明这里极有可能引起内存泄露。这样一来,再去代码层排查就容易多了。

import objgraph
a = [1, 2, 3]
b = [4, 5, 6]
a.append(b)
b.append(a)
objgraph.show_refs([a])

而另一个非常有用的函数,是 show_backrefs()。下面同样为示例代码和生成图,你可以自己先阅读一下:

import objgraph
a = [1, 2, 3]
b = [4, 5, 6]
a.append(b)
b.append(a)
objgraph.show_backrefs([a])

相比刚才的引用调用图,这张图显得稍微复杂一些。不过,我仍旧推荐你掌握它,因为这个 API 有很多有用的参数,比如层数限制(max_depth)、宽度限制(too_many)、输出格式控制(filename output)、节点过滤(filter, extra_ignore)等。所以,建议你使用之前,先认真看一下文档。

Viewpoint #

From #

24 | 带你解析 Python 垃圾回收机制

什么是内存泄漏

什么是内存泄漏

什么是内存泄漏? #

这里的泄漏,并不是说你的内存出现了信息安全问题,被恶意程序利用了,而是指程序本身没有设计好,导致程序未能释放已不再使用的内存。

内存泄漏也不是指你的内存在物理上消失了,而是意味着代码在分配了某段内存后,因为设计错误,失去了对这段内存的控制,从而造成了内存的浪费。

Viewpoint #

From #

24 | 带你解析 Python 垃圾回收机制

选择性的归纳学习

Content #

休谟及英国传统经验主义者认为信念来自习惯,他们认为我们从体验和经验观察中学会了一般化。

与休谟的观念相反,对婴儿行为的研究表明,我们的思维模式使我们对经验进行选择性的一般化。(也就是说,在某些领域进行选择性的归纳学习,而在其他领域保持怀疑态度。)

例如,当小孩看到一群人中某个人的照片并被要求猜出这群人中其他人的特点时,他有能力选择把哪些特点一般化。拿一张过度肥胖的人的照片给一个小孩,告诉他这个人是某个部落的人,让他描述这个部落的其他人,他(很有可能)不会草率地得出这个部落的所有人都有体重问题的结论,但他可能对肤色做出一般化描述。如果你给他看深色皮肤的人的照片,让他描述与这个人同部落的其他人,他会猜测他们都是深色皮肤。

Viewpoint #

From #

JVM的轻量级锁与重量级锁

Content #

内置锁在Java中被抽象为监视器锁(monitor)。在JDK 1.6之前,监视器锁可以认为直接对应底层操作系统中的互斥量(mutex)。这种同步方式的成本非常高,包括系统调用引起的内核态与用户态切换、线程阻塞造成的线程切换等。因此,后来称这种锁为“重量级锁”。

轻量级锁是相对于重量级锁而言的。使用轻量级锁时,不需要申请互斥量,仅仅将Mark Word中的部分字节CAS更新指向线程栈中的Lock Record,如果更新成功,则轻量级锁获取成功,记录锁状态为轻量级锁;否则,说明已经有线程获得了轻量级锁,目前发生了锁竞争(不适合继续使用轻量级锁),接下来膨胀为重量级锁。Mark Word是对象头的一部分;每个线程都拥有自己的线程栈(虚拟机栈),记录线程和函数调用的基本信息。

Viewpoint #

From #

优势序位

Content #

什么是优势序位或“啄食等级”(peck order,因最初用以描述母鸡的情况而得名)?

在许多动物群体中,尤其是豢养的动物,但有时也包括野生动物,个体能记住对方的特征,它们也知道在搏斗中自己能够击败谁,以及通常谁能够打败它们。它们“知道”哪些个体大概能击败它们,因此遇到这些个体时往往不战而降。结果,博物学家就能够把优势序位或“啄食等级”(peck order,因最初用以描述母鸡的情况而得名)形象地描绘出来——在这种等级分明的社会里,每一个个体都清楚自己的地位,因此没有超越自己身份的想法。当然,有时也发生真正的全力以赴的搏斗,而且有时有些个体能够赢得升级,取得超过其顶头上司的地位。但总的说来,等级低的个体自动让步的后果是,真正持久的搏斗很少发生,重伤情况也很少见。

Viewpoint #

From #

自私的基因