安宥之声音懒洋洋的,说着说着还打了个哈欠,一点老师样都没有:“我就不自我介绍了,你们应该都认识,不认识的现在上网查。”
“我旁边这位是我的……助教。”他说完,自己都有点绷不住,只能咳嗽一声忍住笑意:“你们只管当他不存在,看我就行。”
鹿柟闻言凉凉地看了他一眼,然后继续事不关己地翘腿坐着,直直地盯着景谅。
底下一直有学生窃窃私语,兴奋地议论着这位新来的年轻“老师”,更有甚者直接掏出手机拍照,因为忘关闪光灯被身边的人制止后才作罢。
安宥之抬手敲了敲投影幕布,全场立刻安静下来。他托着下巴开口道:“听说上学期郭教授已经上完多线程编程了?很好,现在我来找一位同学现场展示一下上学期的学习成果。”
他装模作样地翻了两下花名册:“——那么,这位叫景谅的同学?”
景谅站起身,整个教室的目光都汇集到了他身上。
他一步步向讲台走去,最后站定在安宥之身边。
安宥之笑眯眯地看着他,伸手点了点自己的电脑:“这是一个典型的线程不安全计数器。景同学,请你为我们现场演示一下,当10个线程各调用1000次increment()后,结果会是多少?并解释原因。”
景谅快速浏览了代码。他心知肚明,正确答案是“结果不确定,但肯定小于10000”。
但他也瞬间看穿,安宥之想让他老老实实地写测试代码,然后得到一个错误结果,再磕磕绊绊地解释原理,从而完成一次“当众出丑”。
景谅没有立刻动手,而是转向安宥之,语气恭敬:
“安老师,直接运行得到错误结果,只能证明它有问题,但无法根治问题。与其演示它‘为什么错’,不如我直接为大家展示‘如何让它对’,并且尝试分析几种主流方案的优劣,您看可以吗?”
安宥之眼底的戏谑消减了几分。
“可以。”他站起身,“贴心”地给景谅让出了自己的座位。
景谅坐在安宥之刚刚坐过的,还有些温热的转椅上。那股安宥之身上奇异的玫瑰花香又出现了,紧紧包裹着他。
景谅感到胸口有点发闷。
鹿柟坐在离他两米开外的地方,一直注视着他。
景谅没有像其他人预想的那样,写一个冗长的多线程测试。他只用了几行代码,就构造了一个极有可能触发问题的场景。
代码运行后,一行字出现在投屏上:
结果输出:预期结果: 10000, 实际结果: 7231。
台下传来一阵恍然大悟的唏嘘。安宥之眯起了眼睛:“嗯,现象看到了。那么,请你修复它吧。”
出乎意料的,景谅并没有修改原始的Counter类,而是对着全班,开始了他的“小讲座”:
“同学们,修复并发问题,需要对症下药。安老师这个案例,病因是count++的非原子性操作。解决方案至少有三种,各有优劣。”
他快速敲击着键盘:
“第一种,同步锁方案。这是最经典的方案,像只有一个洗手间,大家必须排队。可靠,但在高并发下,性能瓶颈明显。”
“第二种,原子变量方案。这是更现代的方案,利用CPU的CAS指令。像大家同时去抢,但只有一个能成功,其他人失败后重试。在高竞争环境下,性能通常优于synchronized。”
最后,他抛出了第三个方案:“其实,最高效的并发,就是没有共享。如果我们使用ThreadLocal,让每个线程操作自己的计数器,最后再汇总,就可以完全避免竞争。”
他展示了使用ThreadLocal的代码草图,虽然在此场景下汇总稍显复杂,但其思想还是让不少认真听课的同学眼前一亮。
景谅同时运行了修复后的三个版本,结果全部正确输出10000。并附上了一句总结:
“所以,选择哪种方案,取决于我们的具体场景:是追求开发速度,是追求极致性能,亦或者是数据特性允许我们做架构上的规避。
“感谢安老师提供的这个经典案例,让我们能一次性横向对比这么多解决方案。”
说完,景谅站起身,向安宥之微微鞠了一躬,接着头也不回地走下台,回到了自己的座位。
教室里安静了几秒,随后响起了热烈的掌声。
刘广平在前排偷偷回头,对他竖起大拇指,景谅只微笑着回应。
安宥之沉默地走到电脑旁,低头看着屏幕。几秒钟后,他抬起头,目光深沉地看向景谅。
两人的视线在空中相撞,像是一场无声的交锋。