您现在的位置是:亿华云 > 人工智能
面试官:有了解过Volatile关键字吗 说说看
亿华云2025-10-09 01:23:06【人工智能】5人已围观
简介概念回顾首先我们回顾一下之前讲的基本概念:内存可见性「内存可见性,指的是线程之间的可见性,当一个线程修改了共享变量时,另一个线程可以读取到这个修改后的值」。重排序为优化程序性能,对原有的指令执行顺序进
概念回顾
首先我们回顾一下之前讲的面试基本概念:
内存可见性
「内存可见性,指的官有关键是线程之间的可见性,当一个线程修改了共享变量时,解过另一个线程可以读取到这个修改后的字说值」。
重排序
为优化程序性能,面试对原有的官有关键指令执行顺序进行优化重新排序。重排序可能发生在多个阶段,解过比如编译重排序、字说CPU重排序等。面试
happens-before
遵循happens-before规则,官有关键JVM就能保证指令在多线程之间的解过顺序性符合执行的预期。
volatile
保证变量的字说「内存可见性」。禁止volatile变量与普通变量「重排序」。面试那么这个内存可见性过程是官有关键怎么样的呢?之前也有给大家演示过具体代码,这里直接给大家总结一下:
所谓内存可见性,解过 当一个线程对volatile修饰的变量进行写操作时,会立即将本地内存中的共享变量刷新到主内存, 同理,当进行读操作时,会立即将本地内存失效,从主内存中读取共享变量的值。
在这一点上,服务器租用volatile与锁具有相同的内存效果,volatile变量的写和锁的释放具有相同的内存语义,volatile变量的读和锁的获取具有相同的内存语义。
禁止重排又是怎么回事呢?
在JSR-133之前的旧的Java内存模型中,是允许volatile变量与普通变量重排序的。想想看,如果可重排,会发生什么?
我们假设有两个线程A和B,一个被volatile修饰的变量a,一个未被修饰的普通变量b,看下边代码:
volatile a = 1;
int b = 2;
public void writer() {
a = 1;
b = 3;
}
public void reader() {
if (a == 1) {
System.out.println(b);
}
}线程A执行writer方法,首先将a设置为1,此时B线程操作reader方法,此时判断a=1,然后进行输出b=2,线程A多b操作设置为3,其实最终结果应该b=3才对,所以这里重排可能会导致普通变量读错的情况。
为了提供一种比锁更轻量级的「线程间的通信机制」,JSR-133决定增强volatile的内存语义:严格限制编译器和处理器对volatile变量与普通变量的云服务器重排序。那么它是怎么禁止的呢❓答案是通过内存屏障,或许你听说过这个概念,下面我们一起看一下。
内存屏障
什么是内存屏障呢?在计算机中,主要分为两种,一种是读屏障(Load Barrier)和写屏障(Store Barrier)。内存屏障有两个作用:
阻止屏障两侧的指令重排序。强制把写缓冲区/高速缓存中的脏数据等写回主内存,或者让缓存中相应的数据失效。(这里的缓存指的是cpu的多级缓存如L1,L2)。我们写的代码最终都是要通过编译器的,那么编译器是怎么实现这个过程的呢?
编译器在生成字节码的时候,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。在Java中,yhM内存屏障插入策略可以保证各平台处理器下程序的站群服务器volatile内存语义正确,具体策略:
在每个volatile写操作前插入一个写屏障。在每个volatile写操作后插入一个写屏障。在每个volatile读操作后插入一个读屏障。在每个volatile读操作后再插入一个读屏障。volatile与普通变量的重排序规则:
如果第一个操作是volatile读,那无论第二个操作是什么,都不能重排序。如果第二个操作是volatile写,那无论第一个操作是什么,都不能重排序。如果第一个操作是volatile写,第二个操作是volatile读,那不能重排序。那么如果第一个操作是普通变量读,第二个是volatile读,可以重排吗?
答案: 可以的。
volatile使用场景
相信在了解以上概念之后,对它应该有一定的认识了, volatile可以保证内存可见性且禁止重排序, 它跟锁又具有相同的内存语义,又被称为轻量级锁。volatile仅仅保证对单个volatile变量的读/写具有原子性,而锁可以保证整个「临界区代码」的执行具有原子性。所以锁更高级一点。但也不是说volatile就不好,作为轻量级的锁,某些场景下还是非常有用的。
我们以双重锁检查单例模式为例,首先我们看一下普通的单例模式:
class Singleton {
private static Singleton instance;
private Singleton() { }
public Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}单线程下你可以这么搞,没毛病,多线程下就不行了,所有我们要加锁,于是双重锁检查下的实现:
class Singleton {
private static Singleton instance;
private Singleton() { }
public static Singleton getInstance() {
if(instance == null) {
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}这样就真没问题了吗疑问?我们知道在new的时候,主要做了三件事:
分配内存变量赋值初始化对象这个过程中,可能会导致指令重排,有可能你会说里边加锁了,上节给大家介绍顺序一致性模型中,我们讲过,在同步模式下临界区内的代码可以发生重排序,所以这里还是有可能发生重排序的,所以最终的这个过程,可能会这样。
线程A执行 分配内存 -> 变量赋值, 线程B执行 判断 instance不为null 开始访问对象,实际上对象还未初始化,所以这时候,我们就要加上volatile。
class Singleton {
private static volatile Singleton instance;
private Singleton() { }
public static Singleton getInstance() {
if(instance == null) {
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}这样在多线程环境下,可以保证其安全性。
结束语
本节内容可能不像之前那么好理解,比较抽象,所以本文也有不足的地方,大家自己可以多查查一些资料,综合理解, 不要去背概念。本节我们提到了锁的概念。
很赞哦!(4)
上一篇: 四、配置网站,填充内容
下一篇: 4、注册门槛低
相关文章
- 在更换域名后,并不是就万事大吉了,我们需要将旧域名做301重定向到新域名上,转移旧域名的权重到新域名上。
- 域名和网址一样吗?域名和网址有什么区别?
- 国际域名转移的费用和处理步骤是什么?
- 在众多公司中,如果我们必须选择一家可信的公司,那当然是信得过的。
- 审核通过的域名将显示在域名竞拍页面,并进入正式拍卖期,买家可以在拍卖周期内出价,加价幅度与拍卖保证金说明,点此查看。
- 旧域名的外链是否会对新建站点产生影响?
- 在此期间,他们每天仍在这里卖大米,在理财方面个人感情有待提高。因为现在是收米的最佳时机。
- 并非一个好米任何人都会给你一个好的价格。那你该如何用以有的好米卖出最理想的价格呢?
- 新手可以注册cc域名吗?cc域名有什么特点?
- 为什么大家都选优质域名?到底存在着什么好处?
热门文章
站长推荐
3、查看排名
(4) 使用何种形式的域名后缀对网页搜索影响不大,但域名后缀也需要考虑方便用户记忆
四、配置网站,填充内容
以上的就是为大家介绍的关于域名的详解
④注册门槛低
当投资者经过第二阶段的认真学习之后又充满了信心,认为自己可以在市场上叱咤风云地大干一场了。但没想到“看花容易绣花难”,由于对理论知识不会灵活运用.从而失去灵活应变的本能,就经常会出现小赢大亏的局面,结果往往仍以失败告终。这使投资者很是困惑和痛苦,不知该如何办,甚至开始怀疑这个市场是不是不适合自己。在这种情况下,有的人选择了放弃,但有的意志坚定者则决定做最后的尝试。
前面这两个步骤都是在本机完成的。到这里还没有涉及真正的域名解析服务器,如果在本机中仍然无法完成域名的解析,就会真正请求域名服务器来解析这个域名了。
3、查看排名