知识卡片
并发正确性的核心判断标准:不是用了哪种同步策略,而是共享资源有没有被真正保护
内容
很多程序员的简历上写着”精通并发编程/熟悉多线程机制”,聊起锁、互斥、线程池、同步、信号量这些名词也头头是道,但真让他们现场写一段简单的并发程序时,能写好的却不多——并发编程的真实难度被严重低估:如果说写好一段普通同步代码的难度是5,并发编程的难度可以达到100,很多看似稳定的程序,一旦面对并发场景依然可能出问题。判断一段代码能不能高质量地处理并发,关键不在于它有没有应用某种具体的同步策略(用没用锁、用没用信号量),而在于代码里的共享资源是否真的得到了保护,具体可以拆解成四类典型的风险点:一是局部变量之外的内存访问都有并发风险(比如访问对象属性、访问静态变量);二是访问共享资源本身有并发风险(比如共享的缓存、数据库);三是被调用方如果没有明确声明是线程安全的,很可能存在并发问题(比如普通的HashMap);四是所有依赖时序的操作,即使组成这个操作的每一步单独看都是线程安全的,整体依然可能存在并发问题(比如先删除一条记录、再把计数减一这样的两步操作,即使删除和减一各自都是线程安全的原子操作,两步之间依然可能被其他线程插入进来破坏预期的顺序结果)。前三种风险通常能通过阅读代码本身比较简单地识别出来,只要培养起对共享资源访问的敏感度就可以;但第四种”依赖时序”的风险往往很难通过单纯看代码发现,甚至这类并发问题的两处相关调用可能根本不在同一个程序里(比如两个系统同时读写同一个数据库、或者并发调用了同一个程序的不同模块)——只要代码里出现了不加锁、访问共享资源的”先做A、再做B”这类逻辑,就应当提高警惕。这个案例给出的核心方法论是:评价并发代码的正确性,应该把注意力从”用了什么同步机制”转移到”共享资源的访问路径有没有被完整地识别和保护”上,前者是手段,后者才是真正决定正确性的本质问题。