所有的编程语言中都有内存模型这个概念,区别于微架构的内存模型,高级语言的内存模型包含了编译器和微架构两局部。我试图理解了Java、C#和Go语言的内存模型,发现内容根本大同小异,只是这些语言在具体实现的时候略有不同。
咱们来看看Java内存模型吧,提到Java内存模型大家对这个图肯定十分相熟:
这张图通知咱们在线程运行的时候有一个内存专用的一小块内存,当Java程序会将变量同步到线程所在的内存,这时候会操作工作内存中的变量,而线程中变量的值何时同步回主内存是不可预期的。但同时Java内存模型又通知咱们通过应用关键词“synchronized”或“volatile”能够让Java保障某些束缚:
“volatile” — 保障读写的都是主内存的变量
“synchronized” —保障在块开始时都同步主内存的值到工作内存,而块完结时将变量同步回主内存
通过以上形容咱们就能够写出线程平安的Java程序,JDK也同时帮咱们屏蔽了很多底层的货色。
但当你深刻理解JVM的时候你会发现基本就没有工作内存这个货色,即内存中基本不会调配这么一块空间来运行你的Java程序,那么工作内存到底是什么货色呢?
这个问题也已经困扰了我很长时间,因为我素来没有从JVM的实现中找到过和主内存同步的代码,因为当应用“volatile”时我仅仅能从源代码中调用了这行语句:
__asm__ volatile ("lock; addl $0,0(%%esp)" : : : "cc", "memory");
而这个指令在局部微架构上的次要性能就是避免指令重排,即这条指令前后的其它指令不会越过这个界线执行[注1]。
在当初的x86/x64微架构中读写内存的一致性都是通过MESI(Intel应用MESI-F,AMD应用MOESI)协定保障[注2],MESI的状态转换图如下:
那Java内存模型中所说的工作内存是什么呢?
我的了解是,首先“工作内存”是一个虚构的概念,而承载这个概念次要是两局部:
1. 编译器2. 微架构
作为编译器必定是执行速度越快越好,所以作为编译器该当尽量减少从内存读数据,如果一个数据在寄存器中,那么间接应用寄存器中的值无疑性能是最高的,但同时这也会导致可能读不到最新的值,这里咱们通过在Java语言中为变量加上“volatile”强制通知编译器这个变量肯定要从内存取得,这时编译器即不会做此类优化【案例见参考资料5(是一个.Net的例子)】。
对于微架构来说,在x86/x64下,CPU会在执行指令时做指令重排,即编译器生成的指令程序和真正在CPU执行的程序可能是不统一的。当咱们用一个变量做信号的时候这种指令重排会带来喜剧,即如果有如下代码:
x = 0;y = 0;i = 0;j = 0;// thread Ay = 1;x = 1;// thread Bi = x;j = y;
下面的代码i和j的值会是多少呢?答案是:“00, 01, 10, 11”都是有可能的。
对于这种状况,如果咱们想得到确定的后果则须要通过“synchronized”(或者j.c.u.locks)来做线程间同步。
所以,我集体对Java内存模型的了解是:在编译器各种优化及多种类型的微架构平台上,Java语言标准制定者试图创立一个虚构的概念并传递到Java程序员,让他们可能在这个虚构的概念上写出线程平安的程序来,而编译器实现者会依据Java语言标准中的各种束缚在不同的平台上达到Java程序员所须要的线程平安这个目标。
最初
私信回复 材料 支付一线大厂Java面试题总结+阿里巴巴泰山手册+各知识点学习思维导+一份300页pdf文档的Java外围知识点总结!
这些材料的内容都是面试时面试官必问的知识点,篇章包含了很多知识点,其中包含了有基础知识、Java汇合、JVM、多线程并发、spring原理、微服务、Netty 与RPC 、Kafka、日记、设计模式、Java算法、数据库、Zookeeper、分布式缓存、数据结构等等。