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 < 5){ 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关键字是字段修饰符,而sync则修改代码块和方法。
其实最根本的一点就是,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 < 100; 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修饰之后一定是原子的。
喜欢文章的朋友可以关注公众号“面经详解”,更多精彩等着你哦!