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