Java内存模型
我之前还以为内存模型,锁,volatile,synchronized写完了。然后发现目录没看到。才发现原来仅仅画了个脑图还没开始写
Java内存模型是一个抽象的概念。不是JVM运行时的内存区域(堆区,方法区,虚拟机栈,本地方法栈,程序计数器)—垃圾回收,运行时空间分配,创建对象过程
1. 并发编程的两个关键问题
- 线程之间如何同步
- 线程之间如何通信
同步指的是线程发生的先后顺序,通信是线程交换信息的方式
通信方式有两种:共享内存和消息传递。(即使是分布式系统,也是这两种交互方式)
Java用的是共享内存这种方式。线程1将数据放到一块共享内存去,线程2去取
2. 内存模型结构
一张图比什么都管用
本地内存是线程私有的(对应:栈),主内存是线程公有的(对应:堆,方法区)。通信的时候,线程A将更改的数据同步到主内存,然后线程b从主内存获取
eg:王胖子直接要玉石,香港商人不给。而是香港商人将玉石放到桌子上,王胖子再从桌子上取(这个情节是为了避免玉石摔了谁负责问题。用来举例通信过程也还可以)
通信:香港商人将玉石给王凯旋
通信过程:①香港商人将玉石放到桌子上,②王凯旋从桌子取玉石
3. 重排序
3.1 重排序是什么?
编译器和处理器(CPU)为了程序性能而使用的一种手段。因为现在CPU是多核或者单机器有多个CPU。 如下面两条语句,我们预期是从上到下执行的。但是系统有却不一定。但是这种即使重排序也没关系。结果不会变化,因为两者没有数据依赖性
int a = 1;
int b = 1;
数据依赖性:如果两个操作访问同一个对象,而且有一个是写操作,那么这两个对象就有数据依赖性。
如果有数据依赖性的两个对象发生了重排序。程序结果可能就不是预期的结果
单处理器且单线程时,编译器和处理器不会违背数据依赖性去重排序。但是其他情况就不考虑遵守了。
3.2 重排序类型
编译器重排序
处理器重排序:指令并行的重排序和内存系统的重排序(写操作延迟载入内存,读直接读取内存)
3.3 重排序对多线程的影响
程序结果和预期的不同,而且多次执行程序,结果也不相同
3.4 JMM对重排序的原则
在不影响结果的情况下。尽可能让编译器和处理器进行重排序(尽可能提高并行度)。其实比较好理解,就是让编译器和处理机尽可能的进行优化呗,前提不影响我最终的结果
4. happens-before
前一个操作(执行的结果)对后一个操作可见 【不是先后顺序。没有规定AB线程哪个要先完成,只是说如果A先完成,A的结果对B可见。如果B先完成,B的结果对A可见】
happens-before原则向程序员屏蔽了重排序的细节
5. 顺序一致性
数据竞争:参考上面的数据依赖,同时有读写操作。没有读写没有通过同步进行排序,就会出现数据竞争,结果就是:程序产生的结果和预期不一致
顺序一致性是个理论参考模型,Java不是顺序一致性。
JMM并不是顺序一致性,只是在正确使用了同步原语(synchronized,voliatile,锁)的情况下,JMM保证结果与顺序一致性结果一致。不想要其带来的坏处。只想要顺序一致性的好处
顺序一致性模型两大特征:(本质就是串行化呗,这样确实不会有多线程带来的各种问题了)
- 一个线程中的操作必须按照程序的顺序进行
- (不管程序是否同步)所有线程都只能看到一个单一的操作执行顺序。在顺序一致性内存模型中,每个操作都必须原子执行且立刻对所有线程可见。
JMM与顺序顺序一致性的差异
- 顺序一致性模型保证单线程 操作按照程序的顺序执行,但是JMM没有这个要求
- 顺序一致性模型保证所有线程只能看到一致的操作执行顺序,而JMM不保证所有线程能看到一致的操作执行顺序。
- JMM不保证对64位的long型和double型变量的写操作具有原子性,而顺序一致性模型保 证对所有的内存读/写操作都具有原子性。
6. 小结
- Java编程思想那本书对JMM就说了一句话:可见性,一致性,顺序性
结合并发编程的艺术。可见性应该就是指happens-before(用volatile关键字);一致性应该是指单线程程序不管执行多少次结果不会变化;顺序性应该指的是在正确使用了同步原语(Synchronized,volatile,锁)的情况加,程序的结果是可以预期的。 - JMM原则:在不影响结果的前提下,尽可能允许编译器和处理器的优化
总目录:Java进阶之路-目录
我本楚狂人,凤歌笑孔丘
李白
博主:五更依旧朝花落
首次发布时间:2020年5月8日16:46:50
末次更新时间: