java并发操作原语与CPU内存指令重排序 Java多线程Cache数据结构JVM
看完之后对自己触动很大,和自己以前的理解有很大的矛盾.当时发站内信给作者的疑惑内容如下:
⒈java内存模型,没有说内存模型的具体结构,如:heap,java stack,method area等.
2.关于"那么,何谓可见性? 多个线程之间是不能互相传递数据通信的,它们之间的沟通只能通过共享变量来进行。Java内存模型(JMM)规定了jvm有主内存,主内存是多个线程共享的。当new一个对象的时候,也是被分配在主内存中,每个线程都有自己的工作内存,工作内存存储了主存的某些对象的副本,当然线程的工作内存大小是有限制的。当线程操作某个对象时,执行顺序如下:
(1) 从主存复制变量到当前工作内存 (read and load)
(2) 执行代码,改变共享变量值 (use and assign)
(3) 用工作内存数据刷新主存相关内容 (store and write) JVM规范定义了线程对主存的操作指令:read,load,use,assign,store,write。当一个共享便变量在多个线程的工作内存中都有副本时,如果一个线程修改了这个共享变量,那么其他线程应该能够看到这个被修改后的值,这就是多线程的可见性问题。"
疑问:为什么要有很多的副本呢?然后再刷新到主存呢?这样做不是效率很低?
java中的方法是值传递,参数是对象的话,只是对象指针的一个副本,如:
public void fun(Object obj){
// 改变obj的属性
obj.property = "abc";
// 改变obj指针的值,对方法外面的obj无影响
obj = new Object();
obj.property = "def";
}
既然每个线程中java stack传递的都是指针(指向heap内的对象),为什么还要复制一个heap内对象的副本呢?
后来看到第二篇的时候
谢谢分享!让我对线程安全的理解更深入一步
今天,特意的查询了这方面的内容,博客地址:http://kenwublog.com/illustrate-memory-reordering-in-cpu
重排序的背景
我们知道现代CPU的主频越来越高,与cache的交互次数也越来越多。当CPU的计算速度远远超过访问cache时,会产生cache wait,过多的cache wait就会造成性能瓶颈。
针对这种情况,多数架构(包括X86)采用了一种将cache分片的解决方案,即将一块cache划分成互不关联地多个 slots (逻辑存储单元,又名 Memory Bank
或 Cache Bank),CPU可以自行选择在多个 idle bank 中进行存取。这种 SMP
的设计,显著提高了CPU的并行处理能力,也回避了cache访问瓶颈。
Memory Bank的划分
一般 Memory bank 是按cache address来划分的。比如 偶数adress 0×12345000 分到 bank 0, 奇数address 0×12345100 分到 bank1。
重排序的种类
编译期重排。编译源代码时,编译器依据对上下文的分析,对指令进行重排序,以之更适合于CPU的并行执行。
运行期重排,CPU在执行过程中,动态分析依赖部件的效能,对指令做重排序优化。
实例讲解指令重排序原理
为了方便理解,我们先来看一张CPU内部结构图。
从图中可以看到,这是一台配备双CPU的计算机,cache 按地址被分成了两块 cache banks,分别是 cache bank0 和 cache bank1 。
最后引用博客作者的一句话:
而实现这个的就是编译期重排和运行时重排序