volatile原理

一、概念


在之前写的笔记中写过volatile被称为轻量级的synchronized,它在多处理器中保证了共享变量的可见性,同时还保证了有序性,如果volatile使用得当,它比synchronized执行的成本更低。

二、volatile的语义


当一个共享变量被volatile修饰之后,这个变量就具备了两层语义:

  • 保证共享变量的可见性:保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

  • 防止局部指令重排序:happens-before规则中的volatile变量规则规定了一个线程先去写一个volatile变量,然后一个线程去读这个变量,那么这个写操作的结果一定对读的这个线程可见。

三、volatile的用法


举个非常简单的一个例子:

public class MyTest {

public static boolean stop = false;

public static void main(String[] args) throws InterruptedException {

//线程1

Thread thread1 = new Thread(() -> {

while (!stop) {

//do something...

}

});

//线程2

Thread thread2 = new Thread(() -> doStop());

thread1.start();

//这里的睡眠是保证线程1先执行

Thread.sleep(1000);

thread2.start();

}

public static void doStop() {

stop = true;

}

}

按照我们原本的设计思想,在线程2开始执行的时候线程1就应该执行了,但是,这个代码的结果却并不是结束,线程1陷入了循环。

为什么呢?明明我们在线程2的时候就把停止条件改为真了,但是为什么不能结束呢?这里就要说说工作内存这个东西了。

工作内存

工作内存是在Java内存模型(Java Memory Model)中提出的,其中讲到,所有的线程共享主内存,每个线程都有自己的工作内存,不是共享的,属于线程私有,一个线程不能访问另一个线程的工作内存,线程之间需要通过主内存来实现线程间的通信。

在每个线程执行的时候,主内存会把线程所需要的数据拷贝一份到线程的工作内存中供线程操作,而线程二开启之后改的只不过是它的工作内存中的数据,所以线程1根本就不知道线程2更改了数据,最后就陷入了循环。

那我们应该怎么解决这个问题呢?当然是加volatile呀,在前面我们说到,加了volatile会保证变量的可见性,那么volatile是如何保证的呢?

从上图我们可以看出,在我们给变量加上了volatile后,我们的线程对volatile变量更改之后,就会执行一个回写操作,把我们的volatile变量同步到主内存中所以我们的线程1的工作内存就可以和主内存进行交互更新,然后就达到了跳出的作用。

有序性

上面我们讲了一下volatile的可见性,现在我们来讲一下volatile的有序性。

早期的CPU的执行顺序其实是顺序执行的,但是因为顺序执行的时候CPU并没有被有效地利用,所以才引入了乱序执行,而乱序执行就引入了指令重排这个概念。

指令重排

指令重排就是把代码作出重排序,来达到提高性能的目的,但是不论怎么重排,它运行的结果都必须和没有重排之前的结果一样,也就是要遵循as-id-serial语义。

那volatile关键字是如何避免指令重排的呢?

如果我们看到了volatileJVM那一层的代码就会发现,有一条指令

__asm__ volatile("lock: addl $0,0(%%rsp)":::"cc","memory");

这句代码既是内存屏障也是代码屏障。这里又要提到一个O2优化的问题了,由于内存屏障属于无效代码,就会被优化掉,但是它又是一个有特殊意义的代码,我们不想它被优化掉,于是就加了一层编译屏障,不让它被优化掉,内存屏障就是屏蔽指令重排的一个东西。

总结:volatile可以保证可见性和有序性,同时也可以在双重检查中用到。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值