问题:
在看单例模式的时候,遇到了一个新的关键字 volatile 。 看了很多介绍,不是很明白
1 在讲指令重排的时候,讲了一个例子,说 在 new 一个对象的时候,分为3个步骤,
<1> 创建内存,
<2>初始化对象,
<3>对象指针指向创建的内存
指令优化的时候,有可能会把 <3> 和 <2> 的顺序颠倒, 如果颠倒的话,那岂不是new对象
的时候就出错了? 但是我写程序从来没有new 出错过,想不明白
2 我查询的是C# 的指令重排与 volatile 关键字, 但是我看文章介绍的时候都会说是 jvm编译
指令的时候, jvm不是 java的东西吗? 但是我在C# 下可以打出 volatile 这个关键字,那C#
的编译器在编译的时候,到底有没有指令重排这种情况?
3 还有文章说 volatile 是 保证线程中更改主线程的值,立即更新到主线程的。 到底这个 volatile 是做什么的?
回答1:
<2>初始化对象是一个内存操作,这可能是一个耗时操作,为了避免CPU在等待这个操作时停顿导致性能下降,编译器会调整指令顺序(开启优化)或者有些架构的CPU会乱序执行指令,也就是将<3>提前执行,这样的话,指针就指向了一个未初始化好的对象。外部得到的单例对象是一个未初始化好的对象,就会引发问题,并不是说new对象的时候出错。这只在多线程场景下才会有小概率出现。指令重排和CPU架构、编译器优化有关,和编程语言无关,不同编程语言都提供了一定的API或赋予关键字一定语义来保证CPU不乱序执行或者编译器不执行指令重排优化。
在C/C++中,volatile是为了提醒编译器在执行的单一线程内, volatile 访问不能被优化掉,但是volatile并不能保证数据是多线程安全的。其他语言也应当是一样的,但是不同语言可能赋予了volatile更多的语义
回答2:
volatile保证可见性和有序性。
可见性可以理解为:在多线程环境中,任何线程对共享变量的修改,其他线程都是可感知的。
有序性就是为了防止重排序,至于你说的那个new对象问题也只是理论上可能出现的,平时开发基本不可能遇到的。