Android并发编程高级面试题汇总最全最细面试题讲解持续更新中👊👊 👀你想要的面试题这里都有👀 👇👇👇
volatile 能否保证线程安全?在DCL上的作用是什么?
这道题想考察什么?
- 是否了解Java并发编程的相关知识?
- 对象创建的过程
考察的知识点
- volatile的原理
- 编译优化
考生应该如何回答
volatile无法保证线程安全,只能保证变量的可见性,并不能保证变量操作的原子性。
原子性指的是一个或者多个操作在 CPU 执行的过程中不被中断的特性。
public class VolatileTest {
public volatile static int count = 0;
public static void main(String [] args){
//开启5个线程
for(int i = 0;i < 5; i++){
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//让count的值自增100次
for(int j = 0;j < 100;j++){
count++;
}
}
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("count= " + count);
}
}
上面代码我们开启5个线程,每个线程当中让静态变量count自增100次。执行之后会发现,最终count的结果值未必是500,有可能小于500。
出现上面情况的原因是因为volatile没有保证原子性。例如A线程获取到count的值为2,此时主存与工作内存数据一致,然后我们执行自增操作,count的值为3,但是主存中的值很有可能被其他线程更新为了8或者其他数目,如果A线程执行更新主存,那数目相当于往下降了。
volatile在单例中的作用
public class SingletonClass {
private volatile static SingletonClass instance = null;
public static SingletonClass getInstance() {
if (instance == null) {
synchronized (SingletonClass.class) {
if(instance == null) {
instance = new SingletonClass();
}
}
}
return instance;
}
private SingletonClass() {
}
}
上述代码中,我们使用到了双重检测和volatile,为什么在双重检测的基础上还需要加上volatile这是因为我们对象创建的部分,可能会因为指令重排发生变化。一般情况下包括三步:
- 分配内存
- 初始化对象
- 将内存地址赋值给引用
如果发生了指令重排可能会导致第二步内容和第三步内容顺序发生变化,即还没初始化的对象已经赋值给引用,再执行相关操作会发生各类异常问题。
总结
因为volatile不能保证变量操作的原子性,所以试图通过volatile来保证线程安全性是不靠谱的。
volatile在DCL上的作用是防止对象发生指令重排而引起的异常问题。
volatile和synchronize有什么区别?(B站 小米 京东)
详细讲解
享学课堂移动互联网系统课程:架构师筑基必备技能《线程与进程的理论知识入门2》
这道题想考察什么?
是否了解Java并发编程的相关知识?
考察的知识点
- volatile的原理
- synchronize的原理
考生应该如何回答
- volatile 只能作用于变量,synchronized 可以作用于变量、方法、对象。
- volatile 只保证了可见性和有序性,无法保证原子性,synchronized 可以保证线程间的有序性(个人猜测是无法保证线程内的有序性,即线程内的代码可能被 CPU 指令重排序)、原子性和可见性。
- volatile 线程不阻塞,synchronized 线程阻塞。
- volatile 本质是告诉 jvm 当前变量在寄存器中的值是不安全的需要从内存中读取;sychronized 则是锁定当前变量,只有当前线程可以访问到该变量其他线程被阻塞。
- volatile标记的变量不会被编译器优化, synchronized标记的变量可以被编译器优化。