Volatile

Volatile

Volatile 是 Java 虚拟机提供轻量级的同步机制
1、保证可见性
2、不保证原子性
3、禁止指令重排

1、保证可见性

public class JMMDemo {
    // 不加 volatile 程序就会死循环!
    // 加 volatile 可以保证可见性
    private volatile static int num = 0;

    public static void main(String[] args) { // main

        new Thread(()->{ // 线程 1 对主内存的变化不知道的
            while (num==0){

            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        num = 1;
        System.out.println(num);

    }
}

2、不保证原子性

// volatile 不保证原子性
public class VDemo02 {

    // volatile 不保证原子性
    private volatile static int num;

    public static void add(){
        num++; // 不是一个原子性操作
    }

    public static void main(String[] args) {

        //理论上num结果应该为 2 万
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000 ; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount()>2){ // main  gc
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName() + " " + num);


    }
}

volatile怎么用

volatile只保证可见性,而加锁机制既可以确保可见性又可以确保原子性。当且仅当满足以下条件下,才应该使用volatile变量:

1、对变量的写入操作不依赖变量的当前值,或者确保只有单个线程变更变量的值。
2、该变量不会于其他状态一起纳入不变性条件中
3、在访问变量的时候不需要加锁。

volatile怎样实现原子性(不使用Lock和synchronized)

使用原子性包装类

image-20200804221042982

// volatile 不保证原子性
public class VDemo02 {

    // volatile 不保证原子性
    // 原子类的 Integer
    private volatile static AtomicInteger num = new AtomicInteger();

    public static void add(){
//         num++; // 不是一个原子性操作
        num.getAndIncrement(); // AtomicInteger + 1 方法, CAS
    }

    public static void main(String[] args) {

        //理论上num结果应该为 2 万
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000 ; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount()>2){ // main  gc
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName() + " " + num);


    }
}

这些类的底层都直接和操作系统挂钩!在内存中修改值!Unsafe类是一个很特殊的存在!

3、禁止指令重排

什么是 指令重排:你写的程序,计算机并不是按照你写的那样去执行的。
源代码-->编译器优化的重排--> 指令并行也可能会重排--> 内存系统也会重排---> 执行
处理器在进行指令重排的时候,考虑:数据之间的依赖性!

int x = 1; // 1
int y = 2; // 2
x = x + 5; // 3
y = x * x; // 4
我们所期望的:1234 但是可能执行的时候回变成 2134 1324 不可能是 4123!

可能造成影响的结果: a b x y 这四个值默认都是 0;

线程A 线程B
x=a y=b
b=1 a=2

正常的结果: x = 0;y = 0;但是可能由于指令重排

线程A 线程B
b=1 a=2
x=a y=b

volatile怎样实现的禁止指令重排?

volatile可以避免指令重排: 利用内存屏障。
内存屏障作用:
1、保证特定的操作的执行顺序!
2、可以保证某些变量的内存可见性 (利用这些特性volatile实现了可见性)

image-20200804223413678

Volatile 是可以保持 可见性。不能保证原子性,由于内存屏障,可以保证避免指令重排的现象产生!

视频参考https://www.bilibili.com/video/BV1B7411L7tE
上一篇:JMM(Java内存模型)
下一篇:彻底玩转单例模式

posted @ 2020-08-05 22:50  Maple_XL  阅读(147)  评论(0编辑  收藏  举报