在Java相关的职位面试中,很多Java面试官都喜欢考察应聘者对Java并发的了解程度,以volatile关键字为切入点,往往会问到底,Java内存模型(JMM)和Java并发编程的一些特点都会被牵扯出来,再深入的话还会考察JVM底层实现以及操作系统的相关知识。

接下来让我们在一个假想的面试过程中来学习一下volitile关键字吧。

1. Java并发这块掌握的怎么样?来谈谈你对volatile关键字的理解吧。

参考答案:

我的理解是,被volatile修饰的共享变量,就会具有以下两个特性:

  1. 保证了不同线程对该变量操作的内存可见性。
  2. 禁止指令重排序。

2. 那你可不可以详细的说一下究竟什么是内存可见性,什么又是重排序?

参考答案:

这个要是说起来可就多了,我就从Java内存模型开始说起吧。Java虚拟机规范试图定义一个Java内存模型(JMM),以屏蔽所有类型的硬件和操作系统内存访问差异,让Java程序在不同的平台上能够达到一致的内存访问效果。简单地说,由于CPU执行指令的速度很快,但是内存访问速度很慢,差异不是一个量级,所以搞处理器的那群大佬们又在CPU里加了好几层高速缓存。

在Java内存模型中,对上述优化进行了一波抽象。JMM规定所有的变量都在主内存中,类似于上面提到的普通内存,每个线程又包含自己的工作内存,为了便于理解可以看成CPU上的寄存器或者高速缓存。因此,线程的操作都是以工作内存为主,它们只能访问自己的工作内存,并且在工作之前和之后,该值被同步回主内存。

说的我自己都有点晕了,用一张图来帮助我们理解吧:

线程执行的时候,将首先从主内存读值,再load到工作内存中的副本中,然后传给处理器执行,执行完毕后再给工作内存中的副本赋值,随后工作内存再把值传回给主存,主存中的值才更新。

使用工作内存和主存,虽然加快了速度,但也带来了一些问题。例如:

i = i + 1;

假设 i 的初始值为 0 ,当只有一个线程执行它的时候,结果肯定是 1 ,那么当两个线程执行时,得到的结果会是 2 吗?不一定。可能会存在这种情况:

线程1: load i from 主存    // i = 0
i + 1 // i = 1
线程2: load i from主存 // 因为线程1还没将i的值写回主存,所以i还是0
i + 1 //i = 1
线程1: save i to 主存
线程2: save i to 主存

如果两个线程遵循上面的执行过程,那么 i 的最终值竟然是 1 。如果最后的写回生效的慢,你再读取 i 的值,都可能会是 0 ,这就是缓存不一致的问题。

接下来就要提到您刚才所问的问题了,JMM主要围绕在并发过程中如何处理并发原子性、可见性和有序性这三个特征来建立的,通过解决这三个问题,就可以解决缓存不一致的问题。而volatile跟可见性和有序性都有关。

3. 那你具体说说这三个特性呢?

1 . 原子性(Atomicity):

在Java中,对基本数据类型的读取和赋值操作是原子性操作,所谓原子性操作就是指这些操作是不可中断的,要做一定做完,要么就没有执行。 例如:

i = 2;
j = i;
i++;
i = i + 1;

以上四个操作, i = 2 是一个读取操作,肯定是原子性操作, j = i 你觉得是原子性操作,但事实上,可以分为两个步骤,一个是读取 i 的值,然后再把值赋给 j ,这已经是两步操作了,不能称为原子操作,i++ 和 i = i + 1 是等效的,读的值,+ 1,然后写回主存,这是三个步骤的操作了。在上面的例子中,最后一个值可能在各种情况下,因为它不会满足原子性。

在本例中,只有一个简单的读取,赋值是一个原子操作,并且只能被分配给一个数字,使用变量来读取变量的值的操作。一个例外是,在虚拟机规范中允许64位数据类型(long和double),它被划分为两个32位操作,但是JDK的最新实现实现了原子操作。

JMM只实现基本的原子性,比如上面的i++操作,它必须依赖于同步和锁定,以确保整个代码块的原子性。在释放锁之前,线程必须将I的值返回到主内存。

2 . 可见性(Visibility):

说到可见性,Java使用volatile来提供可见性。当一个变量被volatile修改时,它的变化会立即被刷新到主存,当其他线程需要读取变量时,它会读取内存中的新值。普通变量不能保证。

事实上,同步和锁定也可以保证可见性。在释放锁之前,线程将把共享变量值刷回主内存,但是同步和锁更昂贵。

3 . 有序性(Ordering)

JMM允许编译器和处理器重新排序指令,但是指定了as-if-串行语义,也就是说,无论重新排序,程序的执行结果都不能更改。例如:

double pi = 3.14;    //A
double r = 1; //B
double s= pi * r * r;//C

上面的语句中,可以按照C - > B - >,结果是3.14,但它也可以按照的顺序B - > - > C,因为A和B是两个单独的语句,并依赖,B,C和A和B可以重新排序,但C不能行前面的A和B。JMM确保重新排序不会影响单线程的执行,但容易出现多线程问题。例如,这样的代码:

int a = 0;
bool flag = false; public void write() {
a = 2; //1
flag = true; //2
} public void multiply() {
if (flag) { //3
int ret = a * a;//4
} }

如果有两个线程执行上面的代码段,线程1首先执行写,然后再乘以线程2。最后,ret的值必须是4?不一定:

如图1和2所示,在写方法中进行重新排序,线程1对第一个赋值为true,然后执行到线程2,ret直接计算结果,然后再执行线程1,这一次的CaiFu值为2,显然是较晚的步骤。

此时要标记加上volatile关键字,重新排序,可以确保程序的“顺序”,也可以基于重量级的同步和锁定来确保,他们可以确保在代码执行的区域内一次性完成。

此外,JMM有一些内在的规律性,也就是说,没有任何方法可以保证有序,这通常称为发生在原则之前。<< jsr-133: Java内存模型和线程规范>>定义了以下事件:

  • 程序顺序规则: 一个线程中的每个操作,happens-before于该线程中的任意后续操作
  • 监视器锁规则:对一个线程的解锁,happens-before于随后对这个线程的加锁
  • volatile变量规则: 对一个volatile域的写,happens-before于后续对这个volatile域的读
  • 传递性:如果A happens-before B ,且 B happens-before C, 那么 A happens-before C
  • start()规则: 如果线程A执行操作ThreadB_start()(启动线程B) , 那么A线程的ThreadB_start()happens-before 于B中的任意操作
  • join()原则: 如果A执行ThreadB.join()并且成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。
  • interrupt()原则: 对线程interrupt()方法的调用先行发生于被中断线程代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测是否有中断发生
  • finalize()原则:一个对象的初始化完成先行发生于它的finalize()方法的开始

第1条程序顺序规则在一个线程中,所有的操作都是有序的,但实际上只要JMM的执行结果允许重新排序,这也是发生的重点——单线程执行结果是正确的,但是也不能保证多线程。

规则2,规则监视器的规则,非常好理解。在锁被添加之前,锁已经被释放,然后它才能继续被锁定。

第三条规则适用于讨论的不稳定性。如果一个行程序编写一个变量,另一个线程读取它,那么在操作之前必须读取写入操作。

第四个规则正在发生。

接下来的几行不会重复。

4. volatile关键字如何满足并发编程的三大特性的?

重新引入volatile变量规则是很重要的:对于一个不稳定域的写,出现之前,然后是对这个volatile字段的读取。本文进行再次说,如果一个变量声明事实上是不稳定,所以当我读了变量,最新的价值总是可以阅读它,这个最新的值意味着无论什么其他线程写操作,该变量将立即更新到主内存,我也可以从主内存读取只写值。也就是说,volatile关键字保证了可视性和有序度。

继续上面的代码示例:

int a = 0;
bool flag = false; public void write() {
a = 2; //1
flag = true; //2
} public void multiply() {
if (flag) { //3
int ret = a * a;//4
} }

当编写一个volatile变量时,JMM在本地内存中刷新与主内存对应的本地内存中的共享变量。

当您读取一个volatile变量时,JMM将使线程对应的本地内存失效,然后线程将从主内存读取共享变量。

Java面试官最常问的volatile关键字的更多相关文章

  1. Java面试官最爱问的volatile关键字

    在Java的面试当中,面试官最爱问的就是volatile关键字相关的问题.经过多次面试之后,你是否思考过,为什么他们那么爱问volatile关键字相关的问题?而对于你,如果作为面试官,是否也会考虑采用 ...

  2. 大厂面试官最常问的@Configuration+@Bean(JDKConfig编程方式)

    大厂面试官最常问的@Configuration+@Bean(JDKConfig编程方式)   现在大部分的Spring项目都采用了基于注解的配置,采用了@Configuration 替换标签的做法.一 ...

  3. java面试官最爱问的垃圾回收机制,这位阿里P7大佬分析的属实到位

    前言 JVM 内存模型一共包括三个部分: 堆 ( Java代码可及的 Java堆 和 JVM自身使用的方法区). 栈 ( 服务Java方法的虚拟机栈 和 服务Native方法的本地方法栈 ) 保证程序 ...

  4. 一线大厂面试官最喜欢问的15道Java多线程面试题

    前言 在任何Java面试当中多线程和并发方面的问题都是必不可少的一部分.如果你想获得更多职位,那么你应该准备很多关于多线程的问题. 他们会问面试者很多令人混淆的Java线程问题.面试官只是想确信面试者 ...

  5. 基础面试,为什么面试官总喜欢问String?

    关于 Java String,这是面试的基础,但是还有很多童鞋不能说清楚,所以本文将简单而又透彻的说明一下那个让你迷惑的 String 在 Java 中,我们有两种方式创建一个字符串 String x ...

  6. java面试官如何面试别人

                                                                                      java面试官如何面试别人(一) j ...

  7. 【JAVA秒会技术之秒杀面试官】秒杀Java面试官——集合篇(一)

    [JAVA秒会技术之秒杀面试官]秒杀Java面试官——集合篇(一) [JAVA秒会技术之秒杀面试官]JavaEE常见面试题(三) http://blog.csdn.net/qq296398300/ar ...

  8. 【面试普通人VS高手系列】volatile关键字有什么用?它的实现原理是什么?

    一个工作了6年的Java程序员,在阿里二面,被问到"volatile"关键字. 然后,就没有然后了- 同样,另外一个去美团面试的工作4年的小伙伴,也被"volatile关 ...

  9. 一个资深java面试官的“面试心得”

    在公司当技术面试官几年间,从应届生到工作十几年的应聘者都遇到过.先表达一下我自己对面试的观点: 1.笔试.面试去评价一个人肯定是不够准确的,了解一个人最准确的方式就是“路遥知马力,日久见人心”.通过一 ...

随机推荐

  1. MySQL学习笔记_4_MySQL创建数据表(下)

    MySQL创建数据表(下) 五.数据表类型及存储位置 1.MySQL与大多数数据库不同,MySQL有一个存储引擎概念.MySQL可以针对不同的存储需求选择不同的存储引擎. 2. showengines ...

  2. Chipmunk碰撞形状:cpShape

    目前有3种碰撞类型: 圆(Circles):最快并且最简单的碰撞形状 线段(Line segment):主要用于静态形状.可以表示斜线(Can be beveled in order to give ...

  3. Android事件总线分发库EventBus3.0的简单讲解与实践

    Android事件总线分发库EventBus的简单讲解与实践 导语,EventBus大家应该不陌生,EventBus是一款针对Android优化的发布/订阅事件总线.主要功能是替代Intent,Han ...

  4. Java 反射之Class用法

    下面示范如果通过Class对象获取对应类的信息: package com.reflect; import java.lang.annotation.Annotation; import java.la ...

  5. mysql进阶(十九)SQL语句如何精准查找某一时间段的数据

    SQL语句如何精准查找某一时间段的数据 在项目开发过程中,自己需要查询出一定时间段内的交易.故需要在sql查询语句中加入日期时间要素,sql语句如何实现? SELECT * FROM lmapp.lm ...

  6. 苹果新的编程语言 Swift 语言进阶(八)--属性

    属性是特定类.结构或枚举的相关值,属性根据作用域不同分为实例属性与类型属性,还可以根据是否存储分为存储属性和计算属性. 1.1 实例属性 为一个类.结构或枚举定义的属性默认属于实例属性,即该属性属于为 ...

  7. Graph Cuts学习笔记2014.5.16----1

    进行了一段时间的论文学习后,现在下载了一些代码,准备从OpenCV跟matlab两个方面着手搭建自己的图像分割平台,计划耗时一个月左右的时间! 昨天去西工大,听了一场Graph Asia的报告,里面有 ...

  8. BT雷人的程序语言

    原文:http://cocre.com/?p=1142  酷壳 这个世界从来都不会缺少另类的东西,人类自然世界如此,计算机世界也一样.编程语言方面,看过本站<6个变态的C语言Hello Worl ...

  9. 八、Join 连接查询

    文档目录 开发中...

  10. 管理xcode插件

    1.打开终端 2.输入     open ~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins 3.出现 4.想删除那个就随意吧 ...