首页 新闻 会员 周边

有关 volatile 的指令重排

0
悬赏园豆:10 [已解决问题] 解决于 2019-05-28 16:46

在看单例模式的时候,遇到了一个新的关键字 volatile 。 看了很多介绍,不是很明白
1 在讲指令重排的时候,讲了一个例子,说 在 new 一个对象的时候,分为3个步骤,
<1> 创建内存,
<2>初始化对象,
<3>对象指针指向创建的内存
指令优化的时候,有可能会把 <3> 和 <2> 的顺序颠倒, 如果颠倒的话,那岂不是new对象
的时候就出错了? 但是我写程序从来没有new 出错过,想不明白

2 我查询的是C# 的指令重排与 volatile 关键字, 但是我看文章介绍的时候都会说是 jvm编译
指令的时候, jvm不是 java的东西吗? 但是我在C# 下可以打出 volatile 这个关键字,那C#
的编译器在编译的时候,到底有没有指令重排这种情况?
3 还有文章说 volatile 是 保证线程中更改主线程的值,立即更新到主线程的。 到底这个 volatile 是做什么的?

百鸟朝凤的主页 百鸟朝凤 | 菜鸟二级 | 园豆:260
提问于:2019-03-29 17:36
< >
分享
最佳答案
0

<2>初始化对象是一个内存操作,这可能是一个耗时操作,为了避免CPU在等待这个操作时停顿导致性能下降,编译器会调整指令顺序(开启优化)或者有些架构的CPU会乱序执行指令,也就是将<3>提前执行,这样的话,指针就指向了一个未初始化好的对象。外部得到的单例对象是一个未初始化好的对象,就会引发问题,并不是说new对象的时候出错。这只在多线程场景下才会有小概率出现。指令重排和CPU架构、编译器优化有关,和编程语言无关,不同编程语言都提供了一定的API或赋予关键字一定语义来保证CPU不乱序执行或者编译器不执行指令重排优化。
在C/C++中,volatile是为了提醒编译器在执行的单一线程内, volatile 访问不能被优化掉,但是volatile并不能保证数据是多线程安全的。其他语言也应当是一样的,但是不同语言可能赋予了volatile更多的语义

收获园豆:10
一罪 | 菜鸟二级 |园豆:250 | 2019-03-29 20:45

那再多线程的应用中,每个引用类型都要加这个关键字,保证不出错么? 那优化岂不是没用了?

百鸟朝凤 | 园豆:260 (菜鸟二级) | 2019-03-31 08:28

@百鸟朝凤: volatile和多线程没有直接关系,除非你认为不能优化,否则没有必要加这个关键字。比如

a = 1;
a = 2;
a = 3;

编译器认为a最后等于3,前面两行代码是没有用的,就会把这两行代码直接忽略掉,正常来说这样的优化没有问题,但在某些场景下就有问题,常见于嵌入式开发。多线程安全不是靠volatile来保证的,需要依赖其他同步机制,比如锁。

PS:上面是基于C/C++的volatile回答的

一罪 | 园豆:250 (菜鸟二级) | 2019-03-31 09:49

@一罪: 那我看单例模式的时候,就要加上这个关键字,有这个必要么

百鸟朝凤 | 园豆:260 (菜鸟二级) | 2019-03-31 09:54

@百鸟朝凤: 没有必要,单例模式最容易出错的就是多线程实例化的时候,而volatile无法保证多线程安全(不是原子操作),还是要采用同步机制,保证只有一个线程去做实例化。

一罪 | 园豆:250 (菜鸟二级) | 2019-03-31 10:04

@一罪: 哦哦,谢谢

百鸟朝凤 | 园豆:260 (菜鸟二级) | 2019-03-31 10:06

@百鸟朝凤: 不客气,可以多用调试器看看程序执行流,看看volatile对程序执行流的影响,会理解的更深刻一点

一罪 | 园豆:250 (菜鸟二级) | 2019-03-31 10:10
其他回答(1)
0

volatile保证可见性和有序性。
可见性可以理解为:在多线程环境中,任何线程对共享变量的修改,其他线程都是可感知的。
有序性就是为了防止重排序,至于你说的那个new对象问题也只是理论上可能出现的,平时开发基本不可能遇到的。

让我发会呆 | 园豆:2929 (老鸟四级) | 2019-03-29 17:48

类中的静态变量属于共享变量么?类在子线程实例化。

支持(0) 反对(0) 百鸟朝凤 | 园豆:260 (菜鸟二级) | 2019-03-31 08:26

@百鸟朝凤: 简单说就是多个线程会对某个变量做修改,共享是对线程的执行方法而言的。

支持(0) 反对(0) 让我发会呆 | 园豆:2929 (老鸟四级) | 2019-04-01 09:25
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册