知识卡片

隐含锁该自动化读锁,而非写锁的获取

普通读书笔记卡

内容

任何加锁模式最怕的就是有人少写一行该写的加锁代码——读数据时忘了获取锁会导致读到过时数据,忘了做版本递增会导致无意中覆盖别人的修改,而离线并发问题极难测试,这类疏漏往往连整套测试都发现不了。隐含锁的解法是把”绝对不能跳过”的加锁动作下沉进框架、层超类型或代码生成里,让开发者压根没机会犯漏加锁这个错误。但作者特别指出一条重要界限:应该被隐含自动化的,是乐观离线锁里的版本存储/检查/递增,以及悲观离线锁里”为读取而获取锁”(互斥读锁、读/写锁中读的部分)和”业务事务结束时释放所有锁”这些动作;唯独不该被隐含自动化的是”为编辑而获取写锁”这个动作(互斥写锁、读/写锁中写的部分)。理由有两点:其一,如果写锁是在幕后被隐含获取的(比如工作单元注册一个被修改的对象时顺带偷偷加锁),一旦这个锁其实早已失效,用户根本无从提前知道、也没法尽早终止这个注定要失败的事务——这正好违背了悲观离线锁”尽早发现冲突、避免用户白干”的初衷;其二,隐含自动获取写锁会把系统并发度限制到最大,剥夺了开发者”要不要为了并发性把某个判断从纯技术层面挪到业务规则层面”这个决策空间。因此框架该做的,只是在提交修改前检查”该有的写锁是不是已经被显式获取”,没获取到就该被视为程序员的失误——建议直接抛出一个并发异常,而不是用可能在生产环境被关闭的断言。可迁移启发:把某个操作自动化、藏进框架里之前,先确认清楚这个操作被自动执行是否会剥夺调用方本该拥有的、及时止损或权衡取舍的决策权——有些步骤该被强制托底,有些步骤则必须留给调用方主动决定,两者不能一概而论地都塞进”自动化”里。

参考来源

- 位置:《企业应用架构模式》第二部分"模式"之"第16章 离线并发模式"之"16.4 隐含锁"(源文件:_epub-src/OEBPS/Text/000201.html) - 结论依据:原文说明"应注意在悲观离线锁的任务中,不包括只为编辑数据而获取锁的操作……由于锁可能无效,隐含地获取它们会带来两个问题。其一,隐含地获取一个写锁……不能预先告知锁的无效性……其二,也是同样重要的,这种锁最大限度地限制了系统的并发度……框架所做的工作就是保证在提交修改前已经获得了写锁。提交时没有获得锁的情况应看成是程序员的失误",直接支撑本卡结论。 - 原始内容:建议不用断言而抛出一个并发异常,因为没有人想在作为产品的系统中关闭断言选项后再发生这样的错误。