Java|面试官问我什么是JMM( 三 )


  • 如果一个变量没有被lock , 就不能对其进行unlock操作 。 也不能unlock一个被其他线程锁住的变量 。
  • 一个线程对一个变量进行unlock操作之前 , 必须先把此变量同步回主内存 。
  • 面试官:讲一下volatile关键字吧
    内心:这可以重头戏呀 , 可不能出岔子~
    很多并发编程都使用了volatile关键字 , 主要的作用包括两点:
    1. 保证线程间变量的可见性 。
    2. 禁止CPU进行指令重排序 。
    可见性
    volatile修饰的变量 , 当一个线程改变了该变量的值 , 其他线程是立即可见的 。 普通变量则需要重新读取才能获得最新值 。
    volatile保证可见性的流程大概就是这个一个过程:


    volatile一定能保证线程安全吗
    先说结论吧 , volatile不能一定能保证线程安全 。
    怎么证明呢 , 我们看下面一段代码的运行结果就知道了:
    /**
    * @author Ye Hongzhi 公众号:java技术爱好者
    **/public class VolatileTest extends Thread {

       private static volatile int count = 0;

       public static void main(String[
    args) throws Exception {
           Vector<Thread> threads = new Vector<>();
           for (int i = 0; i < 100; i++) {
               VolatileTest thread = new VolatileTest();
               threads.add(thread);
               thread.start();
                   //等待子线程全部完成
           for (Thread thread : threads) {
               thread.join();
                   //输出结果 , 正确结果应该是1000 , 实际却是984
           System.out.println(count);//984
       

       @Override
       public void run() {
           for (int i = 0; i < 10; i++) {
               try {                //休眠500毫秒
                   Thread.sleep(500);
                catch (Exception e) {
                   e.printStackTrace();
               
               count++;
           
       


    为什么volatile不能保证线程安全?
    很简单呀 , 可见性不能保证操作的原子性 , 前面说过了count++不是原子性操作 , 会当做三步 , 先读取count的值 , 然后+1 , 最后赋值回去count变量 。 需要保证线程安全的话 , 需要使用synchronized关键字或者lock锁 , 给count++这段代码上锁:
    private static synchronized void add() {
       count++;


    禁止指令重排序
    首先要讲一下as-if-serial语义 , 不管怎么重排序 , (单线程)程序的执行结果不能被改变 。


    推荐阅读