什么是Volatile

Volatile是java的一个关键字,是java虚拟机提供轻量级的同步机制

三大特性

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排

它是怎样保证可见性的

场景:当主内存中有一个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后,可以在该变量的读写操作前和后都加上一个内存屏障,可以避免指令重排的现象产生