2024年最全面试官最爱的 volatile 关键字,这些问题你都搞懂了没?,字节跳动超高难度三面java程序员面经

最后

毕竟工作也这么久了 ,除了途虎一轮,也七七八八面试了不少大厂,像阿里、饿了么、美团、滴滴这些面试过程就不一一写在这篇文章上了。我会整理一份详细的面试过程及大家想知道的一些问题细节

美团面试经验

美团面试
字节面试经验
字节面试
菜鸟面试经验
菜鸟面试
蚂蚁金服面试经验
蚂蚁金服
唯品会面试经验
唯品会

因篇幅有限,图文无法详细发出

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

lock:将主内存中的变量锁定,为一个线程所独占。

unclock:将lock加的锁定解除,此时其它的线程可以有机会访问此变量。

read:将主内存中的变量值读到工作内存当中。

load:将read读取的值保存到工作内存中的变量副本中。

use:将值传递给线程的代码执行引擎。

assign:将执行引擎处理返回的值重新赋值给变量副本。

store:将变量副本的值存储到主内存中。

write:将store存储的值写入到主内存的共享变量当中。

通过上面Java内存模型的概述,我们会注意到这么一个问题,每个线程在获取锁之后会在自己的工作内存来操作共享变量,操作完成之后将工作内存中的副本回写到主内存,并且在其它线程从主内存将变量同步回自己的工作内存之前,共享变量的改变对其是不可见的。

即其他线程的本地内存中的变量已经是过时的,并不是更新后的值。volatile保证可见性的原理是在每次访问变量时都会进行一次刷新,因此每次访问都是主内存中最新的版本。所以volatile关键字的作用之一就是保证变量修改的实时可见性。

即,volatile的特殊规则就是:

read、load、use动作必须连续出现。

assign、store、write动作必须连续出现。

所以,使用volatile变量能够保证:

每次读取前必须先从主内存刷新最新的值。

每次写入后必须立即同步回主内存当中。

也就是说,volatile关键字修饰的变量看到的是自己的最新值。线程1中对变量v的最新修改,对线程2是可见的。

禁止指令重排序优化:


volatile boolean isOK = false;

//假设以下代码在线程A执行

A.init();

isOK=true;

//假设以下代码在线程B执行

while(!isOK){

sleep();

}

B.init();

A线程在初始化的时候,B线程处于睡眠状态,等待A线程完成初始化的时候才能够进行自己的初始化。这里的先后关系依赖于isOK这个变量。

如果没有volatile修饰isOK这个变量,那么isOK的赋值就可能出现在A.init()之前(指令重排序,Java虚拟机的一种优化措施),此时A没有初始化,而B的初始化就破坏了它们之前形成的那种依赖关系,可能就会出错。

知识拓展:指令重排序:

概念:指令重排序是JVM为了优化指令,提高程序运行效率,在不影响 单线程程序 执行结果的前提下,尽可能地提高并行度。编译器、处理器也遵循这样一个目标。注意是单线程。多线程的情况下指令重排序就会给程序带来问题。

不同的指令间可能存在数据依赖。比如下面的语句:

int l = 3; // (1)

int w = 4; // (2)

int s = l * w; // (3)

面积的计算依赖于l与w两个变量的赋值指令。而l与w无依赖关系。

重排序会遵守两个规则:

as-if-serial规则:as-if-serial规则是指不管如何重排序(编译器与处理器为了提高并行度),(单线程)程序的结果不能被改变。这是编译器、Runtime、处理器必须遵守的语义。

happens-before规则:

程序顺序规则:一个线程中的每个操作,happens-before于线程中的任意后续操作。

监视器锁规则:一个锁的解锁,happens-before于随后对这个锁的加锁。

volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。

传递性:如果(A)happens-before(B),且(B)happens-before(C),那么(A)happens-before(C)。

线程start()规则:主线程A启动线程B,线程B中可以看到主线程启动B之前的操作。也就是start() happens-before 线程B中的操作。

线程join()规则:主线程A等待子线程B完成,当子线程B执行完毕后,主线程A可以看到线程B的所有操作。也就是说,子线程B中的任意操作,happens-before join()的返回。

中断规则:一个线程调用另一个线程的interrupt,happens-before于被中断的线程发现中断。

终结规则:一个对象的构造函数的结束,happens-before于这个对象finalizer的开始。

概念:前一个操作的结果可以被后续的操作获取。讲直白点就是前面一个操作把变量a赋值为1,那后面一个操作肯定能知道a已经变成了1。

happens-before(先行发生)规则如下:

虽然,(1)-happensbefore ->(2),(2)-happens before->(3),但是计算顺序(1)(2)(3)与(2)(1)(3)对于l、w、area变量的结果并无区别。编译器、Runtime在优化时可以根据情况重排序(1)与(2),而丝毫不影响程序的结果。

volatile使用场景:

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

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

如果正确使用volatile的话,必须依赖下以下种条件:

也可以这样理解,就是上面的2个条件需要保证操作是原子性操作,才能保证使用volatile关键字的程序在并发时能够正确执行。

第一个条件的限制使 volatile 变量不能用作线程安全计数器。虽然增量操作(i++)看上去类似一个单独操作,实际上它是一个由(读取-修改-写入)操作序列组成的组合操作,必须以原子方式执行,而 volatile 不能提供必须的原子特性。

实现正确的操作需要使 i 的值在操作期间保持不变,而 volatile 变量无法实现这点。

在以下两种情况下都必须使用volatile:

1、状态的改变。

2、读多写少的情况。

具体如下:

// 场景一:状态改变

/**

  • 双重检查(DCL)

*/

public class Sun {

private static volatile Sun sunInstance;

private Sun() {

}

public static Sun getSunInstance() {

if (sunInstance == null) {

synchronized (Sun.class) {

if (sunInstance == null){

sunInstance = new Sun();

}

}

}

return sunInstance;

}

}

// 场景二:读多写少

总结

我个人认为,如果你想靠着背面试题来获得心仪的offer,用癞蛤蟆想吃天鹅肉形容完全不过分。想必大家能感受到面试越来越难,想找到心仪的工作也是越来越难,高薪工作羡慕不来,却又对自己目前的薪资不太满意,工作几年甚至连一个应届生的薪资都比不上,终究是错付了,错付了自己没有去提升技术。

这些面试题分享给大家的目的,其实是希望大家通过大厂面试题分析自己的技术栈,给自己梳理一个更加明确的学习方向,当你准备好去面试大厂,你心里有底,大概知道面试官会问多广,多深,避免面试的时候一问三不知。

大家可以把Java基础,JVM,并发编程,MySQL,Redis,Spring,Spring cloud等等做一个知识总结以及延伸,再去进行操作,不然光记是学不会的,这里我也提供一些脑图分享给大家:

希望你看完这篇文章后,不要犹豫,抓紧学习,复习知识,准备在明年的金三银四拿到心仪的offer,加油,打工人!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

紧学习,复习知识,准备在明年的金三银四拿到心仪的offer,加油,打工人!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 25
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,`volatile`关键字用于修饰变量,表示该变量是易变的,即每次使用该变量时,都必须从内存中读取最新的值,而不是使用缓存值。它的主要作用是保证多线程之间对该变量的可见性和禁止指令重排。 下面是一些可能的面题: 1. `volatile`关键字的作用是什么? 答:`volatile`关键字用于保证多线程之间对该变量的可见性和禁止指令重排。它可以强制线程从主内存中读取变量的值,而不是使用线程本地的缓存值,以确保多个线程之间的变量值是一致的。 2. `volatile`关键字与`synchronized`关键字有什么区别? 答:`volatile`关键字和`synchronized`关键字都可以用于多线程编程中,但它们的作用不同。`volatile`关键字用于保证变量的可见性和禁止指令重排,而`synchronized`关键字用于保证线程的安全性和同步性。在使用`synchronized`关键字时,同一时刻只能有一个线程进入临界区,而`volatile`关键字有这种限制。 3. 什么情况下应该使用`volatile`关键字? 答:`volatile`关键字适用于以下情况: - 变量被多个线程共享; - 变量的值在多个线程之间发生了变化; - 对变量的读操作不依赖于变量的当前值; - 对变量的写操作不会覆盖其它线程对变量的修改。 4. `volatile`关键字是否可以保证线程安全? 答:`volatile`关键字不能保证线程安全,它仅仅保证了变量在多个线程之间的可见性和禁止指令重排。要保证线程安全,还需要使用`synchronized`关键字或其它线程安全的机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值