一、 如何保证不乱序,也就是保证有序性
1、 硬件内存屏障
注意:这是inter X86
1.1 sfence
store fence 在sfence指令前面的写操作必须在sfence指令后边的写操作前完成
如图:如果没有sfence ,是不能保证操作1在操作2执行前就执行完的,有了sfence才能保证操作1和操作2的顺序
1.2 lfence
load fence 指令前的读操作必须在lfence指令后边的读操作执行前执行
如图: 如果没有lfence 不能保证读操作1 和 读操作2 的顺序 ,有了lfence就可以保证了
1.3 mfence
mix fence 是集合了sfence和lfence的所有作用于一身,mfence前边的读写操作必须在mfence后边读写操作开始之前完成
如图:mfence可以保证 在执行读操作2和写操作2之前 必须执行完读操作1和写操作1
1.4 其他
除了fence外,还有一些原子指令 ,比如 lock xxx
这些原子指令是一个Full Barrier,执行时会锁住内存子系统来保证执行顺序
甚至跨多个cpu
2.、JVM级别规范
注意:jvm这只是jvm的规范,具体实现要看虚拟机怎么实现
2.1 LoadLoad 屏障
保证读操作的顺序,LoadLoad前边的读操作必须在LoadLoad后边的读操作前完成
如图: LoadLoad能保证 读操作2和者读操作3执行前 必须执行完读操作0 和读操作1
2.2 StoreStore 屏障
对比LoadLoad 保证写操作有序
2.3 LoadStroe 屏障
对比LoadLoad 保证读操作和写操作有序
2.4 StoreLoad 屏障
对比 LoadLoad 保证写操作和读操作有序
3、volatile 实现细节
3.1 字节码层面
这里用到jclasslib 前边文章讲过怎么安装
public class VolatileTest {
int i;
volatile int x;
public static void main(String[] args) {
}
}
jclasslib 可以看出来:
i的修饰符是 0x0000[]
x的修饰符是 0x0040[volatile]
很明显,字节码层面就只是给加了volatile的属性加了一个标识
3.2 JVM层面
在写操作 和 读操作前后加了屏障
前后加了屏障,保证了顺序性
volatile类型变量修改之后会立即写回内存 ,也就是从工作内存写回到主内存(JMM知识)
3.3 操作系统硬件层面
需要使用hsdis进行反汇编,也就是把class编译成汇编指令。深入理解Java虚拟机(第三版),448页有个例子,这里就不叙述了, 其实作为了解即可 ,底层也是通过sfence lfence mfence 或者 lock 实现的。
windows x86 是用过lock 实现的
这里需要使用hsdis ,没有太大必要去使用 ,如果有兴趣可以用一下,不是很难,以后有时间写一篇简单应用的文章。
4、synchronize 实现细节
4.1 字节码层面
public class TestSync {
synchronized void m(){
}
void n(){
synchronized (this){
}
}
}
同样使用jclasslib看
可以看出来 方法是用了一个标识符, 代码块使用了monitorenter monitorexit 语句
4.2JVM层面
c、c++调用操作系统提供的同步机制
4.3操作系统硬件层面
x86:使用的lock cmpxchg 等 指令
只是作为了解,其实工作中很少能用到,感兴趣的可以根据具体的关键字进行查询
按照字节码、jvm、操作系统硬件层面 这三个层面理解即可
最后写一个面试题大家看看能回答上来吗?
- 描述一个一个对象的创建过程
- 对象在内存中的存储布局
- 对象头具体包含什么
- 对象是怎么定位的
- 对象怎么分配的
- Object o = new Object() 在内存中占用了多少字节
下一篇以这个题为例 写一篇文章
共同学习,写下你的评论
评论加载中...
作者其他优质文章