最近发现自己在产品思维上有所欠缺,并且刚刚读完《java并发编程艺术这本书》,于是决定从程序员的需求角度大概写写自己对JMM的理解,希望对大家有所帮助。
了解一个解决方案首先得明白它处理的问题,通过背景知识,我们可以更好的理解它产生的原因,以及其中所包含的思想
在单个处理器的处理速度的提升已经不在明显的时候,人们便尝试使用多种方法来提高计算机的运算效率,其中包括
但同时一个全新概念的引入,往往会伴随着诸多问题等待着这些伟大的先驱者去解决。
10/13/16dc57096e9b6482?w=1003&h=716&f=png&s=54796)
当多个处理器要使用和修改主内存中同一块区域时,可能会导致不同处理器中的数据不一样,如何将不同CPU寄存器、高速缓存中的数据进行同步,保证数据的一致性,运算的正确性的同时保证处理器的效率是设计着需要考虑的问题。在硬件上,各个处理器都遵循了各自的一致性协议,来解决这些问题
# CPU1 CPU1 a = 2 b = 100 b = 100 a = 2 print(a + b) print(a + b) 复制代码
重排序能够有效的提高处理器的运算效率,但是重排序有时候会让结果发生错误,尤其是在多核环境下,会发生很多奇妙的问题,猜猜下面两个CPU输出的结果
# CPU1 CPU2 a = 10 b = 20 b = a + 100 a = b + 100 print(f"b = {b}") print(f"a = {a}") 复制代码
如果已多次运行以上程序会发现,在终端中竟然出现了 a = 100, b = 100的结果, 原因是底层处理器”自以为是“的重排序使得我们运行的程序变成如下
# CPU1 CPU2 a = 10 b = 20 print(f"b = {b}") print(f"a = {a}") b = a + 100 a = b + 100 复制代码
为解决多核处理器所带来的问题,不同的设计着提供了不同的方案,但对于程序员,了解这些策略并编写正确的程序具有很大的难度
在生活中我们常常做不了甲方,但对于JAVA的设计者们来说,我们就是甲方,而且我们的要求不多,也并不过分
当我们程序员写出了正确的代码的前提下,希望程序按照我们所设想的代码顺序执行,并输出正确的结果
所以在设计的时候,处理器的内存模型和编程语言的内存模型都会以顺序一致性作为参考,接下我们具体的谈谈顺序一致性的具体要求
当我们处理多线程问题的时候,常常会遇到如下数据竞争的问题:
作为程序员我们期望,当我们约定了线程1的操作先于线程2执行(反之亦然),此时这个程序便成为了一个没有数据竞争的程序,同时我们也可以称之为正确同步的程序,该程序的执行将具有 顺序一致性
当我们运行了多个线程的时候,顺序一致性向我们保证了:
![原子执行]( user-gold-cdn.xitu.io/2019/
class SynchronizedExample { int a = 0; boolean flag = false; public synchronized void writer() { a = 1; flag = true; } public synchronized void reader() { if (floag) { int i = a; ... } } } 复制代码
在上面实例代码中,writer()和reader()方法,分别在线程1和线程二中执行,我们也期望同步块中的执行也满足线程的 顺序一致性 。
在满足 顺序一致性 的条件下,我们希望代码的执行效率越快越好
我们希望JAVA的设计者们能够提供安全且易实现的机制,满足顺序一致性以及执行效率,并且让我们的写出来的代码具有良好的可读性。
从上文我们可以看到,顺序一致性与执行效率是相互矛盾的,所以JAVA的设计者们做了如下决定
在满足执行结果与程序员期望结果的前提下,尽可能的减少顺序一直性对内存模型的束缚
为此,提出了happens-before原则来描述这种设计理念,本文不会针对这个概念做深入的解释,但我们可以这么理解,设计者们为程序员创造了一个程序是按照顺序一致性执行的桃花源,它不会违背程序员所期望的结果,但实际过程却有所不同。
java设计者们为达到可读性和易实现性,提供了一下关键字和方法
JMM就是由这些小部件组成