volatile相关面试点
2026/9/16 23:36:54 网站建设 项目流程

目录

并发编程的三个重要特性

1.volatile 的原子性、可见性、有序性

2.volatile 深入解析

3.volatile 和 synchronized


并发编程的三个重要特性

1. 原子性:在一次操作或者多次操作中,要么所有的操作全部得到执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行。

2. 可见性:当一个线程对共享变量进行了修改,那么另外的线程可以立即看到修改后的最新值。

3. 有序性: 指程序代码在执行过程中的先后顺序,由于Java在编译期以及运行期的优化,导致了代码的执行顺序未必就是开发者编写代码时的顺序。

1.volatile 的原子性、可见性、有序性

在java中,对基本数据类型读取赋值操作都是原子性的,对引用类型的变量读取和赋值的操作也是原子性的。

PS:1. 多个原子性的操作在一起就不是原子性操作了。

2. 简单的读取和操作都是原子性,将一个变量赋给另外一个变量的操作不是原子性的。

总结:volatile 关键字不具备保证原子性的语义。

Java提供了三种方式来保证可见性:

1. 使用关键字 volatitle ,当一个变量被 volatile 关键字修饰时,对于共享资源的读取操作会直接在主内存中进行(当然也会缓存到工作内存中,当其他线程对该共享资源进行了修改,则会导致当前线程在工作内存中的共享资源失效,所以必须从主内存中再次获取),对于共享资源的写操作当然是先要修改工作内存,但是修改结束后会立即将其刷新到主内存中。

2. 使用 synchronized 关键字,这个就不多说了

3. 通过JUC提供的显式锁 Lock 。

总结: volatile 关键字具有保证可见性的语义。

有序性同可见性一样,Java 使用了一模一样的这三种方式来保证有序性。

总结: volatile 关键字具有保证顺序性的语义。

2.volatile 深入解析

被 volatile 修饰的实例变量或者类变量具备两层含义:

1. 保证了不同线程之间对共享变量操作时的可见性。也就是当一个线程修改 volatile 修饰的变量,另外一个线程会立即看到最新的值。

2. 禁止对指令进行重排序的操作。

volatile 的变量规则: 对一个变量的写操作要早于对这个变量的读操作。

通过对 OpenJDK 下的 unsafe.cpp 源码的阅读,会发现被 volatile 修饰的变量存在于一个 “lock;” 的前缀。

“lock;” 前缀实际上相当于是一个内存屏障,该内存屏障会为指令的执行提供以下几个保障:

确保指令重排序时不会将其后面的代码排到内存屏障之前。

确保指令重排序时不会将其前面的代码排到内存屏障之后。

确保在执行到内存屏障修饰的指令时前面的代码全部执行完成。

强制将线程工作内存中值得修改刷新到主内存中。

如果是写操作,则会导致其他线程工作内存(CPU Cache)中的缓存数据失效。

volatile 的使用场景

使用 volatile 要充分利用它的可见性和有序性。

但它不能代替 synchronized ,因为 volatile 不具备原子性。

3.volatile 和 synchronized

一、使用上的区别

volatile 只能用于修饰实例变量或者类变量,不能用于修饰方法以及方法参数和局部变量、常量等。

synchronized不能用于对变量的修饰,只能用于修饰方法或者语句块。

volatile 修饰的变量可以为 null ,synchronized同步语句块的 monitor 对象不能 为 null 。

二、对原子性的保证

volatile 无法保证原子性。

由于 synchronized 是一种排他机制。因此被 synchronized 关键字修饰的同步代码是无法被中途打断的,因此能保证代码的原子性。

三、对可见性的保证

两者均可以保证共享资源在多线程间的可见性,但是实现机制完全不同。

synchronized 借助于JVM 指令 monitor enter 和 monitor exit 对通过排他的方式使得同步代码串行化,在 monitor exit 时所有共享资源都将会刷新到主内存中。

相比较于 synchronized 关键字, volatile 使用机器指令(偏硬件)“lock;”的方式迫使其他线程工作内存中的数据失效,不得不到主内存中进行再次加载。

四、对有序性的保证

volatile 关键字禁止 JVM 编译器以及处理器对其进行重排序,所以它能够保证有序性。

synchronized 修饰的同步方法也可以保证有序性,不过是通过程序的串行化执行换来的。

五、其他

volatile 不会使线程陷入阻塞。

synchronized 关键字会使线程进入阻塞状态。

六、volatile 深入解析

被 volatile 修饰的实例变量或者类变量具备两层含义:

1. 保证了不同线程之间对共享变量操作时的可见性。也就是当一个线程修改 volatile 修饰的变量,另外一个线程会立即看到最新的值。

2. 禁止对指令进行重排序的操作。

volatile 的变量规则: 对一个变量的写操作要早于对这个变量的读操作。

通过对 OpenJDK 下的 unsafe.cpp 源码的阅读,会发现被 volatile 修饰的变量存在于一个 “lock;” 的前缀。

“lock;” 前缀实际上相当于是一个内存屏障,该内存屏障会为指令的执行提供以下几个保障:

确保指令重排序时不会将其后面的代码排到内存屏障之前。

确保指令重排序时不会将其前面的代码排到内存屏障之后。

确保在执行到内存屏障修饰的指令时前面的代码全部执行完成。

强制将线程工作内存中值得修改刷新到主内存中。

如果是写操作,则会导致其他线程工作内存(CPU Cache)中的缓存数据失效。

volatile 的使用场景

使用 volatile 要充分利用它的可见性和有序性。

但它不能代替 synchronized ,因为 volatile 不具备原子性。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询