数据竞争与顺序一致性保证
当程序未正确同步时,就会存在数据竞争。java内存模型规范对数 据竞争的定义如下:
在一个线程中写一个变量,
在另一个线程读同一个变量,
而且写和读没有通过同步来排序。
当代码中包含数据竞争时,程序的执行往往产生违反直觉的结果(前一章的示例正是如此)。如果一 个多线程程序能正确同步,这个程序将是一个没有数据竞争的程序。
JMM对正确同步的多线程程序 的内存一致性做了如下保证:
如果程序是正确同步的,程序的执行将具有顺序一致性(sequentially consistent)--即程序的执行 结果与该程序在顺序一致性内存模型中的执行结果相同(马上我们将会看到,这对于程序员来说是一个极 强的保证)。这里的同步是指广义上的同步,包括对常用同步原语(lock,volatile和final)的正确使 用。
顺序一致性内存模型
顺序一致性内存模型是一个被计算机科学家理想化了的理论参考模型,它 为程序员提供了极强的内存可见性保证。顺序一致性内存模型有两大特性:
一个线程中的所有操作必须按照程序的顺序来执行。
(不管程序是否同步)所有线程都只能看到一个单一的操作执行顺序。在顺序一致性内存模型中,每 个操作都必须原子执行且立刻对所有线程可见。
顺序一致性内存模型为程序员提供的视图如下:
在概念上,顺序一致性模型有一个单一的全局内存,这个内存通过一个左右摆动的开关可以连 接到任意一个线程。同时,每一个线程必须按程序的顺序来执行内存读/写操作。从上图我们可以看出, 在任意时间点最多只能有一个线程可以连接到内存。当多个线程并发执行时,图中的开关装置能把所有线 程的所有内存读/写操作串行化。
为了更好的理解,下面我们通过两个示意图来对顺序一致性模型 的特性做进一步的说明。
假设有两个线程A和B并发执行。其中A线程有三个操作,它们在程序中的 顺序是:A1->A2->A3。B线程也有三个操作,它们在程序中的顺序是:B1->B2->B3。
假设这两个线程使用监视器来正确同步:A线程的三个操作执行后释放监视器,随后B线程获取同一个监视 器。那么程序在顺序一致性模型中的执行效果将如下图所示:
现在我们再假设这两个线程没有做同步,下面是这个未同步程序在顺序一致性模型中的执行示意图: