volatile原理

目录

一.可见性问题

二.有序性问题

三.volatile原理

1.如何保证可见性

2.如何保证有序性


一.可见性问题

退不出的循环:

main 线程对 run 变量的修改对于 t 线程不可见,导致了 t 线程无法停止
    static boolean run = true;

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while(run){
                // ....
            }
        });
        t.start();
        Thread.sleep(1);
        run = false; // 线程t不会如预想的停下来
    }

原因:

1.初始状态, t 线程刚开始从主内存读取了 run 的值到工作内存。

2. 因为 t 线程要频繁从主内存中读取 run 的值, JIT 编译器会将 run 的值缓存至自己工作内存中的高速缓存中, 减少对主存中 run 的访问,提高效率
3. 1 秒之后, main 线程修改了 run 的值,并同步至主存,而 t 是从自己工作内存中的高速缓存中读取这个变量 的值,结果永远是旧值

二.有序性问题

Result是一个对象,有一个属性r1用来保存结果,问可能的结果有几种?

    int num = 0;
    boolean ready = false;
    // 线程1 执行此方法
    public void actor1(Result r) {
        if(ready) {
            r.r1 = num + num;
        } else {
            r.r1 = 1;
        }
    }
    // 线程2 执行此方法
    public void actor2(Result r) {
        num = 2;
        ready = true;
    }

分析:

(1)线程1 先执行,这时 ready = false,所以进入 else 分支结果为 1

(2)线程2 先执行 num = 2,但没来得及执行 ready = true,线程1 执行,还是进入 else 分支,结果为1

(3)线程2 执行到 ready = true,线程1 执行,这回进入 if 分支,结果为 4(因为 num 已经执行过了)

(4)线程2 执行 ready = true,切换到线程1,进入 if 分支,相加为 0,再切回线程2 执行 num = 2

情况4的现象就是发生了指令重排序(是JIT编译器在运行时的优化)。

指令重排的前提是,重排指令不能影响结果,例如:

// 可以重排的例子
int a = 10; // 指令1
int b = 20; // 指令2
System.out.println( a + b );
// 不能重排的例子
int a = 10; // 指令1
int b = a - 5; // 指令2

三.volatile原理

volatile 变量的写指令后会加入写屏障
volatile 变量的读指令前会加入读屏障

1.如何保证可见性

写屏障( sfence )保证在该屏障之前的,对共享变量的改动,都同步到主存当中
而读屏障( lfence )保证在该屏障之后,对共享变量的读取,加载的是主存中最新数据

2.如何保证有序性

写屏障会确保指令重排序时,不会将写屏障之前的代码排在写屏障之后。
读屏障会确保指令重排序时,不会将读屏障之后的代码排在读屏障之前。
    public void actor2(Result r) {
        num = 2;
        ready = true; // ready 是 volatile 赋值带写屏障
        // 写屏障
    }
    public void actor1(Result r) {
        // 读屏障
        // ready 是 volatile 读取值带读屏障
        if(ready) {
            r.r1 = num + num;
        } else {
            r.r1 = 1;
        }
    }

注意:volatile不能解决原子性问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值