上次线程池已经说过了,从今天开始一起了解下JVM内存模型详解。
(一)容易误解的部分
老铁很容易把JAVA的内存区域、JAVA的内存模型,GC分代回收的老年代和新生代也容易搞混,绕进去绕不出来。学习多线程之前一定要搞明白这些问题,可能在你的内心一直认为多线程就是一个工具,所有的底层都是C++来写的,没办法去看,为什么要有java,java其实就是屏蔽了底层的复杂性。
GC内存区域
堆的概念,老年代,新生代,Eden,S0,S1
JAVA的内存区域
JVM运行时的区域:java编译生成class,线程共享部分(方法区,堆内存),线程独占部分(虚拟机栈,本地方法栈,程序计数器)
JAVA的内存模型(概念)
针对多核多CPU,多线程而制定的一套规范规则,不是一种开发技术。
(二)多线程中的问题
所见非所得(你看到的并不是所想的)、
无法肉眼去检测程序的准确性(多线程下,完全看不出来正常不正常)。
不同的运行平台有不同的表现。
错误很难重现。
(三)工作内存和主内存
主内存
创建一个对象在堆里面,也可以称之为主内存,不仅仅是在堆,存在一个对象X,就存在主内存
工作内存
线程运行在工作内存, 虚拟机栈,程序计数器,CPU,高速缓存。
工作内存和主内存只是一个逻辑上的划分,概念上的东西。
奇妙的现象
主内存的flag传输到工作内存flag的时候,存在CPU缓存的情况,CPU缓存可能导致非常短的时间内不一致,本身CPU厂家底层是要做一致处理的,但是存在短时间内的不一致。
(四)指令重排
介绍
Java语言规范JVM线程内部维持顺序或语义,即只要程序的最终结果与它顺序化情况的结果相等,那么指令的执行顺序可以与代码逻辑顺序不一致,这个过程就叫做指令的重排序。
意义
使指令更加符合CPU的执行特性,最大限度的发挥机器的性能,提高程序的执行效率。
重排序,只能保证单个线程的,如果是多线程的话,就没有爆发保证重排序。
java // 线程1 a = d; b = 2 // 线程2 c = a; d =3 //重排序后 //线程1 b = 2 ; a =d; //线程2 d = 3 ; c =a; ¨G0G java public class VisibilityDemo2 { // 状态标识 (不用缓存) private volatile boolean flag = true; ¨K38K } ¨G1G java import java.util.concurrent.TimeUnit; public class VisibilityDemo1 { // 状态标识 private static boolean is = true; ¨K39K } ¨G2G java public class VisibilityDemo { private volatile boolean flag = true; ¨K40K }
启动线程的操作与线程中的第一个操作同步
对于每个属性写入默认值(0,false,null)与 每个线程对其操作的同步
线程T1的最后操作与线程T2发现线程T1已经结束同步(isAlive,join可以判断线程是否终结)
如果线程T1终端了T2,那么线程T1的中断操作与其他所有线程发现T2倍中断了同步,通过抛出InterruptedException异常,或者调用Thread.interrupted 或者 Thread.isInterrupted。
(八)Happyens-before先行发生原则
介绍
强调两个有冲突的动作之间的顺序,以及定义数据征用的发生时机。
原则
同一个线程里面对数据做了变动,后面的动作可以及时的看到,其实还是可见性。
某个monitor上的unlock动作 happens-before 同一个monitor上后续的lock动作。
对某个volatile 字段的写操作 happens-before 每个后续对该 volatile 字段的读操作。
在某个线程对象上调用start() 方法 happens-before 该启动了的线程中的任意动作。
某个线程中的所有动作 happens-before 任意其他线程成功从该线程对象上的join() 中返回。
如果某个动作 a 在happens-before 动作 b,b 在happens-before 动作 c,则 a happens-before c。
(九) final 在JMM中的处理
final在该对象的构造函数中设置对象的字段,当线程看到该对象时,将始终看到该对象的final字段的正确构造版本。
如果在构造函数中设置字段后发生读取,则会看到该final字段分配的值,否则它将看到默认值。
读取该共享对象的final成员变量之前,先要读取共享对象。
通常static final 是不可以修改的字段。然而System.in, System.out 和 System.err 是static final 字段,遗留原因,必须允许通过set方法改变,这些字段称为写保护,以区别于普通的final字段。
PS:使用了volatile,unlock和lock的时候,就可以保证代码不进行重排序。内存模型java进阶的一个核心点,这个理解了,其实比写多少年的业务代码要重要很多。
领取专属 10元无门槛券
私享最新 技术干货