一、基本概念
- 程序:代码,为了完成某一任务的,代码序列 静态
- 进程:程序完成某一任务的一次运行 动态
- 线程:一个进程由多个线程组成
二、JVM与线程
JVM什么时候启动? 答:类被调用 JVM线程包含其他线程(main)
三、JVM的内存区域
方法区,堆、程序计数器、本地方法栈,java栈 等区域。详情请看图1-1
- 方法区:类信息、常量、static、JIT(及时编译器)(共享区域)
- 类信息:由classLoader(类加载器)
- 常量:成员变量
- static变量:静态变量
- JIT:即时编译
- 堆:实例对象、GC(共享区域)OOM指的是存在内存溢出的风险
- java栈:运行时的内存模型 图1-2 OOM指的是存在内存溢出的风险
- 程序计数器(PC):java线程的私有数据,这个数据就是记录执行下一指令的地址
- 本地方法栈:与虚拟机的native有关系
四、java的内存模型 JMM(规范,抽象模型) 图1-3
- 主内存:共享数据,引用类型的对象放在主内存
- 工作内存:私有数据,线程就是在工作内存中存在的,基本数据类型直接存在工作内存,引用的类型的的地址放在工作内存。
- 工作方法:
- A线程如果要修改私有数据,会在A线程工作空间找,没找到将会去主内存中找到并复制到工作空间,并进行修改。
- B线程如果要修改共享数据,同理上面,但是区别于复制到工作空间后修改完毕会再刷新(赋给)主内存。为什么要这样设计?为了确保信息的保密性,个人理解相当于你和别人借钱要买某保险,你一定是先将钱借到手,再去买,而不是带着业务员去借钱对象的家里面买保险
五、硬件内存架构与java内存模型
- 硬件内存架构 图1-4 CPU只的是CPU运行区(处理器内存,并不是4核 2核CPU概念,指的是CPU分配时间片)
- CPU缓存一致性:内存中存在变量A=1,CPU1获取A的值之后要进行修改A=2,此时CPU2获取变量A有可能是1也可能是2.因为CPU修改完毕后会通过cache(缓存)刷新到内存中。如果还没有保存到内存中那么CPU2将获取的A=1,反之A=2.。此时会出现并发不同步(不安全)
- 解决的问题
-
- 总线加锁:此种方法很影响CPU的吞吐量,虽然能保证数据的一致性,但是牺牲了大量的时间,类似于java中的方法使用sysnchronize,同一时间只能一个CPU用。
- cache缓存一致性协议(Intel协议,MESI协议):CPU从cache获取变量到寄存器中修改完毕后,会将cache中的变量设置一个状态 cache Line为无效类似于state=0,其他CPU如果检测到cache Line为0时则直接从内存中获取变量的value,防止其他CPU从cache读取脏数据
- 线程与硬件处理器 图1-5 进程>线程>操作系统OS>CPU
- java内存模型与硬件内存架构的关系 图1-6
- 图中可以看到是JMM(虚拟机数据模型)与CUP内存架构是交叉关系,所以会造成数据不一致性
- java内存模型的必要性
- java内存模型的作用:规范内存数据(主内存)与工作空间(工作内存)的交互
六、并发编程的三个重要特征
- 原子性:不可分割的,同生共死。
- 可见性:一个线程只能读写自己的工作空间(工作内存)的cache
- 有序性:程序中的顺序不一定就是执行顺序,
- 编译的时候会重排,
- 指令也会重排,
- 为了提高效率
- 例子:int a=1;int b=2;int c=0; c=a+b; int d=0; 因为变量c比较复杂,所以效率慢,排序后先执行d=0;再去执行c=a+b;
-
-
- 单线程重排规则(编译优化):as-if-seria规则,重排后不影响执行结果
- 多线程重排规则(指令优化):happens-before规则,程序次序原则(程序的结果不能变化),锁定原则(后一次加锁必须等前一次解锁),Volatile原则(霸道原则),传递原则(A语句再B语句前执行,最后C执行,得出的结论是A必须在C语句前执行。A--B--C A--C)。
-
七、JMM(java内存模型)三大特性的保证
- JVM的原子性
- int x = 10; 基础类型是存在工作空间,所以写入的时候就是存在原子性。如果是私有数据具有原子性,如果是共享数据则没有原子性。先读后写则不存在原子性
- int y = x;没有原子性。原因:把数据x读取到工作空间(原子性) 把x的值写到y(原子性)但是读写后则不存在原子性
- int i++;没有原子性。原因:读i到工作空间,然后写入i+1,内存刷新结果,单个执行都具有原子性,但是放到一起不具备原子性。
- int Z=z+1;没有原子性,原因:先将z读到工作空间,然后计算z+1,赋值给Z,内存刷新结果。
-
- 结论:多个原子性的操作合并到一起就没有原子性。怎么保证原子性呢?
- 保证方式:
- Synchronized
- JUC Lock的lock加锁
- JMM的可见性
- 工作空间修改了变量后,刷新给内存后,怎么保证其他工作空间队内存的变量的可见性
- 保证可见性方式:
-
- 使用Volatile:再JMM模型上实现MESI协议。
- Sysnchronized:加锁
- JUC LOCK的lock加锁
- JMM的有序性
- 保证方式:
- Volatile
- Synchronized
Happens-before规则
八、总结
- JVM内存区域和JMM(抽象java内存模型)的关系
- JMM和硬件的关系
- JMM和并发编程三个重要特征 *(有序性 as-if-seria和happens-before)
欢迎大家来提问,如果有不理解的可以再评论区提出,我会定时解惑。
同舟共济,新海同航
图1-1 图1-2 图1-3
图1-4 图1-5 图1-6
java中的start如何调用run方法?
1、模板模式、线程是对应操作系统的线程,一一对应的。因为需要将你想做的逻辑让操作系统去执行。
start---start0---native---pthread_create(java_start){
}
void{
java_start jni反射调用java run
}