Java------多线程_高级主题_happenbefore_volatile(十四)
当写的代码没有按照期望的顺序执行,因为编译器和CPU会尝试指令重排,使代码更快的运行。
1.从内存拿到一条指令,将指令进行解码翻译
2.从寄存器获取对应值
3.执行代码,执行指令
4.将结果写会寄存器
happenbefore指令重排会对多线程的结果产生影响。
编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段(一些代码慢,一些代码快,先执行快的,即使这部分代码在后面,减少CPU的空闲,提高执行效率)。
存在虚拟机层面、和硬件层面两种。
编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会噶变存在数据依赖关系的两个操作的执行顺序。
案例代码:
package cooperation;
/**
* 指令重排:代码的执行顺序和预期不一致
* 目的:提高性能
*/
public class ThreadHappenBfore {
//变量1
private static int i;
//变量2
private static boolean flag ;
public static void main(String[] args) throws InterruptedException {
for (int y= 0;y<100;y++){
i = 0;
flag = false;
//线程一:读取数据
Thread thread = new Thread(() -> {
i = 1;
flag = true;
});
//线程二:更改数据
Thread thread2 = new Thread(() -> {
if (flag){
i *= 1;
}
//存在指令重排
if (i ==0){
System.out.println("指令重排:---?"+i);
}
});
thread.start();
thread2.start();
//插队线程
thread.join();
thread2.join();
}
}
}
有时会出现打印 指令重排:—?+1的情况。意味着i*=1在i0之后执行,或者还没有将结果返回至寄存器,i0就已经执行完毕的情况。
volitale:
volatile保证线程间变量的可见性,简单地说就是当线程A对变量X进行了修改后,在线程A后面执行的其他线程能看到变量X的变动。
1.线程对变量进行修改后,要立刻会写到主内存
2.线程对变量读取的时候,要从主内存中读,而不是缓存。
各线程的工作内存间彼此独立,互不可见,在线程启动的时候,虚拟机为每个线程分配一块工作内存,不仅包含线程内部定义的局部变量,也包含了线程所需要使用的共享变量的副本,为了提高执行效率。
volatile是不错的机制,但是volatile不能保证原子性。不能保证取、计算、存一致,现在不多使用。
案例代码:如果num没有加volatile,则会一直执行,不会停止。加了volatile,num=1后就会停。
/**
* volatile用于保证数据的同步,也就是可见性,但不能保证一致性
*/
public class ThreadVolatile {
private volatile static int num = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while (num==0){
//死循环,模拟cpu忙碌
}
}).start();
Thread.sleep(1000);
//没有加volatile,即便赋值为1也不会停
num = 1;
}
}
粒度小,只保证数据的同步,开销比synchronized小,轻量级synchronized。
volatile和synchronized都能够保证指令重排。