java 知乎面试题_知乎 面试题之Java volatile关键字

本文深入解析Java volatile关键字的作用,包括保证线程可见性,避免指令重排,并通过代码示例展示其与synchronized的区别,探讨long/double原子性及Volatile对其的影响。
摘要由CSDN通过智能技术生成

Java面试中经常会被问到volatile关键字,面对这个问题,可以回答如下:

volatile关键字是为了保证线程安全,线程安全意味着一个方法或类实例可以被多个线程同时使用,而不会出现任何问题。它提供的功能主要有两点:保证可见性

防止指令重排

紧接着面试官可能让我们解释可见性。

可见性:如果两个线程在不同的处理器上运行,则每个线程可能具有自己的共享变量的本地副本。如果一个线程修改其值,则更改可能不会立即反映在主内存中的原始线程中。如果使用Volatile,则可以保证变量的值始终从主内存中读取,而不是从Thread的本地缓存中读取。这样就保证了不同线程之间的变量是彼此可见的。

在阐述可见性的时候可以想想下图。下图中处理器1,2就保存着变量的副本,导致变量的值不统一。

为了更加清晰的理解可见性,用代码进行验证:public class VolatileTest {

private static  int MY_INT = 0;

public static void main(String[] args) {

new ChangeListener().start();

new ChangeMaker().start();

}

static class ChangeListener extends Thread {

@Override

public void run() {

int local_value = MY_INT;

while ( local_value 

if( local_value!= MY_INT){

System.out.println("改变 MY_INT " + MY_INT);

local_value= MY_INT;

}

}

}

}

static class ChangeMaker extends Thread{

@Override

public void run() {

int local_value = MY_INT;

while (MY_INT <5){

System.out.println("增加 MY_INT 至" + (local_value+1));

MY_INT = ++local_value;

try {

Thread.sleep(500);

} catch (InterruptedException e) { e.printStackTrace(); }

}

}

}}

上面的代码中除了主线程之外还有两个线程,一个是ChangeListener,监听对于MY_INT的改变,另一个是ChangeMaker,对MY_INT执行累加操作,这两个操作都在本地有MY_INT的副本,如果不使用Volatile关键字修饰,ChangeMaker对MY_INT的改变ChangeListener就监听不到,执行的结果就如下面所示:增加 MY_INT 至1增加 MY_INT 至2增加 MY_INT 至3增加 MY_INT 至4增加 MY_INT 至5

如果Volatile修饰了MY_INT变量,执行的结果如下所示:增加 MY_INT 至1改变 MY_INT 1增加 MY_INT 至2改变 MY_INT 2增加 MY_INT 至3改变 MY_INT 3增加 MY_INT 至4改变 MY_INT 4增加 MY_INT 至5改变 MY_INT 5

可以看到每一次的改变,ChangeListener都可以监听到。

讲完了可见性,如果面试官对并发这块感兴趣的话,可能会延伸到synchronized,讲讲synchronized和volatile的区别。

详细的回答可以参考下面:Java中的volatile关键字是字段修饰符,而synchronized则修改代码块和方法。

volatile仅在线程内存和主内存同步变量是使用,synchronized同步线程内存和主内存变量并且可以释放和获得monitor。

其实最根本的一点就是,synchronized可以实现原子性,也就说一个时刻只允许一个线程进入临界区。Volatile不能实现原子性。public  class VolatileTest {

private  volatile static int MY_INT = 0;

public static void main(String[] args) {

ExecutorService es = Executors.newFixedThreadPool(5);

for (int i = 0; i 

es.submit(new Runnable() {

@Override

public void run() {

System.out.println(++VolatileTest.MY_INT);

try {

Thread.sleep(40);

} catch (InterruptedException e) {

e.printStackTrace();

}

};

});

}

es.shutdown();

}}

上面的代码结果有可能并不能累加到100,这就是Volatile不能保证原子性的结果。

回答了上面的问题,你以为就完了吗?面试官可能会紧接着再问一个问题:long和double的读写时原子的吗?如果使用Volatile修饰之后呢?

long和double的读写不一定是原子的,但是volatile修饰之后一定是原子的。

发布于 2019-10-19

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值