乐趣区

深入讲解并发编程模型之顺序一致性篇

前面两篇文章讲解过了深入并发编程模型基本概念和重排序,还没有阅读过前面两篇文章的建议阅读下:

  • 深入讲解并发编程模型之概念篇
  • 深入讲解并发编程模型之重排序篇

什么是顺序一致性

顺序一致性,简单理解为: 就是程序的执行顺序和它编写的顺序一致

顺序一致性模型

顺序一致性内存模型是一个被计算机科学家理想化了的理论参考模型,它为程序员提供了极强的内存可见性保证。顺序一致性内存模型有两大特性:

  • 一个线程中的所有操作必须按照程序的顺序来执行

也就是说,一个线程看到的程序的执行顺序,是和它的代码的编写顺序一致的。比如在一个线程中,代码逻辑是先编写 A,再编写 B,最后再编写 C,那么在一致性模型的约束下,程序的执行顺序是 A -> B -> C。是不会发生指令重排序的。(不要搞混,重排序是在 JMM 模型下实现的,并不是一致性模型下实现。它俩的区别下文会讲。)

例如,在一致性模型约束下,程序执行顺序和代码编写顺序一致。即使是 A、B、C 操作执行顺序互不影响程序结果,也不会改变程序执行顺序。

  • 所有线程都只能看到一个单一的操作执行顺序

在顺序一致性内存模型中,每个操作都必须原子执行且立刻对所有线程可见。也就是说,多个线程之间,不管程序是否同步,它们看到的执行顺序是一致的。不可能 A 线程看到的程序执行顺序和 B 线程看到的执行顺序是不一致的。

例如下图,不管程序是否执行了同步,1、2 线程看到的整体程序执行顺序是一致的。

比如进行了同步,线程 1、2 看到的执行顺序是 A -> B -> C -> D -> E -> F,或者 D -> E -> F -> A -> B -> C。不会出现不一致。如果未进行了同步,那么 ABC、DEF 的执行顺序整体是有序的,也就是说会遵守 B 或 C 不会在 A 之前执行,但是可能会在 DEF 之前执行,具体看实际的线程获取的 CPU 时间片的分配。

但是,如果未进行程序同步,由于顺序一致性模型要求线程的操作具有原子性,也就说线程的任何操作都是马上对其它线程可见的。所以,即使代码没有进行线程同步,多个线程看到的代码执行的顺序也是一致。

顺序一致性和 JMM 的对比

顺序一致性,保证程序的执行顺序一致,JMM 会根据一定规则(比如遵循 happens-before 原则),会对程序执行指令进行重排序,达到对编译器和处理器优化的目标。在 JMM 模型下,在不影响程序执行结果的前提下,编译器、处理器会对指令进行重排序。关于重排序已经分析过,可以阅读这篇文章:深入讲解并发编程模型之重排序篇

如图所示基于顺序一致性模型和 JMM 模型下,在进行线程同步时程序的执行顺序。在顺序一致性模型下,每一个线程程序执行顺序已经确定,但是在 JMM 模型下,每一个线程在临界区的程序执行的顺序未知(虚线框起来的是临界区代码),因为 JMM 进行了指令重排。

总结

这篇文章讲到这里,已经和大家分析完了并发模型中的一致性问题。实际上,一致性模型我们很少用,因为这样编译器和处理器无法对程序做到优化,在 Java 中我们使用的是可以进行指令重排序的 JMM 模型。

还有一个原因,前面说过,顺序一致模型要求线程的每一个操作都具有原子性,也就是说,读写都会操作主存,这样的效率肯定会比 JMM 模型下先对线程本地内存操作的方式要低的多。

退出移动版