从单例模式挖到内存模型(三)----volatile关键字

volatile这个单词在英文中的意思是:易变的,不稳定的。这个关键字在java中就是表示某个变量是不稳定的,java会有自己的方式去处理这种变量。

 

java对volatile的定义:

java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排它锁单独获得这个变量。

 

当一个变量定义为 volatile 之后,将具备两种特性:

1,保证此变量对所有的线程的可见性。当一个线程修改了这个变量的值,volatile保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。但普通变量做不到这点,当对非 volatile 变量进行读写的时候,每个线程先从主内存拷贝变量工作内存中,变量在工作内存中被修改后,不会立即同步到主内存。

2,禁止指令重排序优化。有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个CPU访问内存时,并不需要内存屏障。

 

volatile 性能:

volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。

 

volatile不保证操作的原子性

volatile不保证操作的原子性,验证的例子很简单,开100个线程给一个数字做i++的操作,数字增加的结果和操作次数并不一致。

volatile不保证操作的原子性的原因:volatile只保证从主内存复制到工作内存的变量是最新的(因为变量改变后立刻从工作内存刷新到主内存),但已经读到工作内存的变量不会因为别的线程刷新了主内存而发生改变。

举例:线程A和线程B都修改变量x=10,x是用volatile修饰的。

结果正确的情况:

1,线程A从主内存中读取变量x,复制到A的工作内存(x=10)。

2,线程A操作x++,并立刻刷新到主内存(主内存x=11)。

3,线程B从主内存读取变量x,复制到B的工作内存(x=11)。

   如果x没有volatile修饰,这个时候线程A可能还没有把x=11刷新到主内存,这个时候线程B得到的x就可能是10。

4,线程B操作x++,并立刻刷新到主内存(主内存x=12)。

结果错误的情况:

1,线程A从主内存中读取变量x,复制到A的工作内存(x=10)。

2,线程B从主内存读取变量x,复制到B的工作内存(x=10)。volatile并不阻塞线程,所以多线程情况下有可能两个线程都从主线程读取了同一个变量。

3,线程A操作x++,并立刻刷新到主内存(主内存x=11)。

4,线程B操作x++,并立刻刷新到主内存(主内存x=11),这个结果并不正确。

 

不能把volatile当成synchronize这样的锁来用,如果要用他来满足线程安全需要符合以下条件:

1,对变量的写操作不依赖于当前值。

2,该变量没有包含在具有其他变量的不变式中。

 

最后,很多高手建议:尽量别用volatile这个关键字,很容易就用错了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值