什么是Volatile
Volatile是java的一个关键字,是java虚拟机提供轻量级的同步机制
三大特性
- 保证可见性
- 不保证原子性
- 禁止指令重排
它是怎样保证可见性的
场景:当主内存中有一个flag变量,线程A先去主内存中read并load到自己的工作内存中使用,然后又有一个线程B去使用了这个变量并改变了这个变量后store并write进了主内存区。如果这个变量不加Volatile关键字,线程A是无法感知线程B或其他线程对这个变量进行的修改,同时也无法获取当前这个变量在主内存中的值
public class VolatileDemo {
private static int num = 0;
public static void main(String[] args) {
new Thread(()->{
while(num == 0){
}
}).start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
num = 1;
System.out.println(num);
}
}
为什么不保证原子性
原子性:不可分割 线程A在执行任务的时候,不能被打扰,也不能被分割,要么同时成功,要么同时失败,即当前线程在操作这个内存中的变量时,其他线程也能操作这个变量,示例代码如下:
public class VoltaileDemo01 {
private volatile static int num = 0;
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int i1 = 0; i1 < 1000; i1++) {
add();
}
}).start();
}
while(Thread.activeCount() > 2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + "" + num);
}
private static void add(){
num++;
}
}
怎样让这个操作变成原子性?
可以用元子类来代替int,因为这些类的底层都直接和操作系统挂钩!在内存中修改值!Unsafe类是一个很特殊的存在
它是怎样禁止指令重排的
什么是指令重排
你写的程序,计算机并不是按照你写的那样去执行的。例如:
A a = new A();
new的操作看字节码文件其实主要有3个步骤:
- 分配内存空间 //1
- 执行构造方法初始化对象 //2
- 将该对象指向这个空间 //3
在编译的时候执行的顺序可能是123 也可能是132或是213等,这就是指令重排
执行过程: 源代码-->编译器优化的重排-->指令并行也可能会重排-->内存系统也会重排-->执行
int x = 1; //1
int y = 2; //2
x = x + 5; //3
y = y * x; //4
期望:1234 可能会变成2134 1324
但不会是4123,因为处理器在进行指令重排的时候,考虑:数据之间的依赖性!
给变量加volatile后,可以在该变量的读写操作前和后都加上一个内存屏障,可以避免指令重排的现象产生

0 评论
评论