知识卡片
竞态来自复合操作
内容
线程安全问题常藏在看似简单的复合操作中:amount++ 看似一个符号,实际是“内存取值、寄存器加1、存值到内存”三条独立 JVM 指令,每条指令本身原子,但组合在一起就不再原子——多个线程交错执行会互相覆盖对方的写入结果。被并发访问、且至少有一个线程会写的共享资源叫临界区资源,访问它的那段代码叫临界区代码段;锁要保护的不是变量本身,而是让临界区代码段整体排他执行,不被其他线程的操作插入打断。
参考来源
- 位置:《Java高并发核心编程.卷2,多线程、锁、JMM、JUC、高并发设计模式》第2章《Java内置锁的核心原理》2.1.1节《自增运算不是线程安全的》、2.1.2节《临界区资源与临界区代码段》(源文件:_epub-src/OEBPS/Text/chapter59.xhtml、chapter60.xhtml)
- 结论依据:原文用10线程各自增1000次、实际结果少了近30%的实验证明“++”非线程安全,并解释自增由“内存取值/寄存器加1/存值到内存”三条独立指令组成,两个原子操作组合后不再具备原子性;进而给出临界区资源、临界区代码段和竞态条件的定义,说明排他访问是解法,因此推出本卡结论。
- 原始内容:一个自增运算符是一个复合操作,至少包括三个JVM指令:“内存取值”“寄存器增加1”和“存值到内存”……两个或者两个以上的原子操作合在一起进行操作就不再具备原子性了……临界区资源表示一种可以被多个线程使用的公共资源或共享数据,但是每一次只能有一个线程使用它……我们必须保证临界区代码段操作具备排他性。