JAVA程序员需要了解的计算机底层知识(2)
CPU的乱序执行
CUP在进行读等待的同时执行其他指令,是CPU乱序的根源,不是乱,是优化提高效率。
比如一个泡茶的过程 :
洗水壶,烧开水,洗茶壶,洗茶杯,拿茶叶,泡茶
但是在实际生活的中,除了烧开水和泡茶两个步骤,其他的步骤并没有必要的顺序,在烧水的过程中,我们可以随意安排洗水壶,洗茶壶,洗茶杯,拿茶叶的步骤。这样就相比于串行化的执行过程,必然会节省时间的成本。
而cpu执行两个没有引用关系的指令的时候,同样也是这个原理。
如何证明会有乱序?
public class T04_Disorder {
private static int x = 0, y = 0;
private static int a = 0, b =0;
public static void main(String[] args) throws InterruptedException {
int i = 0;
for(;;) {
i++;
x = 0; y = 0;
a = 0; b = 0;
Thread one = new Thread(new Runnable() {
public void run() {
//由于线程one先启动,下面这句话让它等一等线程two. 读着可根据自己电脑的实际性能适当调整等待时间.
//shortWait(100000);
a = 1;
x = b;
}
});
Thread other = new Thread(new Runnable() {
public void run() {
b = 1;
y = a;
}
});
one.start();other.start();
one.join();other.join();
String result = "第" + i + "次 (" + x + "," + y + ")";
if(x == 0 && y == 0) {
System.err.println(result);
break;
} else {
//System.out.println(result);
}
}
}
JAVA中乱序执行是否会影响代码?
一个对象的创建过程大体是 先分配内存空间,执行构造方法初始化,最后把堆中的地址和栈中的本地变量表建立关系。(注意:便宜理解大体是这样)new一个对象会向下图这样,JVM会把创建对象转换成如下的汇编码。
在单线程的情况下,两个没有任何关联的代码执行顺序的混乱是不会有影响的。
在多线程的情况下就会引发问题。比如在 单例模式 下访问创建对象的getInstance静态方法并发创建,但是如果是第一次访问这个方法创建新的实例过程中,并发线程同时访问这个方法。而第一个线程由于指令乱序的问题astore_1的指令会提前执行(把引用提前给本地变量表,实际上这个实例并没有完成初始化),但是并发线程会认为这个类不为null,会直接使用这个未完成初始化的实例,就会给程序带来问题。
CPU层面如何禁止乱序?
答案:内存屏障。
对某部分内存做操作时前后添加屏障,屏障前后的操作时不可以乱序执行的。
比如说:指令1和指令2中间添加一个屏障。指令1个指令2之间执行顺序不可以互换。
那么inter是如何实现内存平常的呢?
答案:使用原语和总线锁来实现的。
原语又是什么?
有三种:
1.sfence,在sfence指令前的写操作当必须在sfence指令后写操作前完成。
2.lfence,在lfence指令前的读操作当必须在lfence指令后的读操作之前完成。
3.mfence,在mfence执行之前的读写操作必须在mfence指令后的读写操作之前完成。
JVM层级禁止乱序的规范?
8个hanppens-before原则 和 4个内存屏障
8个hanppens-before原则就不说了,就是人类的正常思维,至于四个内存屏障:
volatile的实现细节
StoreStoreBarrier //写写屏障
volatile 写操作
StoreLoadBarrier //写读屏障
StoreStoreBarrier的意思是说在 volatile 写操作 之前加了一个屏障,这个屏障的作用是是 volatile写操作 之前如果有写操作,必须等之前的写操作对其他处理器可见。
StoreLoadBarrier的意思是说在 volatile 写操作 之后的读操作,必须在写完之后才能读取。
LoadLoadBarrier
volatile读操作
LoadStoreBarrier
LoadLoadBarrier的意思是在 volatile读操作 之前的读操作必须完成才能执行 volatile读操作
LoadStoreBarrier的意思是 volatile读操作 之后的写操作被刷出前,必须等 volatile读操作 读取完毕。
hotspot如何实现的禁止乱序?
就是在volatile修饰的变量 指令 前后加上 lock 指令来做具体实现的。
操作系统OS
简单的说,OS其实是一个特殊的软件,又软又硬,是所有软件的老大。
OS一般会分成两层,一层是kernel(内核),内核最主要的作用就是管理硬件(内存,硬盘,CPU …), 一层是外围程序和应用程序打交道,,一边管理着硬件资源,一边对外提供服务。
OS访问现在分了两个不同的级别
1.应用程序能够访问的级别。
2.内核能访问的级别。
简单的说 就是层架os的安全性,有一些层,内核太可以访问,用户态不能访问,如果用户态要访问,需要通过内核来访问。
内核空间,就是内核才能访问的空间。运行内核的空间。用户是不可能把这块内存干掉的。
cpu分不同的指令级别
linux内核跑在ring 0级, 用户程序跑在ring 3,对于系统的关键访问,需要经过kernel的同意,保证系统健壮性
内核执行的操作 - > 200多个系统调用 sendfile read write pthread fork
JVM -> 站在OS老大的角度,就是个普通程序
JAVA程序员需要了解的计算机底层知识(1)
JAVA程序员需要了解的计算机底层知识(3)
该文章属于自己整理的笔记
如果我有理解错误的地方,欢迎大家留言指正,谢谢。
戏如人生 纯手打。