JAVA面试题——volatile的理解


需要了解的内容: JMM内存模型描述

一、简介(请你谈谈对volatile的理解)

首先,volatile是Java虚拟机提供的轻量级的同步机制,他基本遵守了JMM的规范。
用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此读取volatile类型的变量时总会返回最新写入的值。
在访问volatile变量时不会执行加锁操作,因此不会使执行线程阻塞。

二、三大特性

volatile主要有三大特性:保证可见性,有序性(即禁止指令重排序),但是不保证原子性。

什么叫保证了可见性?

可见性:一个线程修改了主物理内存的值,其他线程马上获取主内存的值。

禁止指令重排序

有序性:为了提高性能,编译器和处理器都会对指令做重排,一般分为:
源代码-> 编译器优化的重排->指令并行的重排->内存系统的重排->最终执行的指令

单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致。
处理器在进行重排序时必须要考虑指令之间的数据依赖性

多线程环境中线程是交替执行的,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测。加入了volatile之后,就可以保证有序性。

不保证原子性

验证不保证原子性代码:

public class volatileDemo {
    public static void main(String[] args) {
        MyData myData = new MyData();

        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for(int j=1;j<=1000;j++){
                    myData.addPlusPlus();
                    myData.addMyAtomic();
                }
            },String.valueOf(i)).start();
        }
        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+"\tAtomicInteger type,final number"+myData.atomicInteger);
    }
}
class MyData{
    volatile int number = 0;//没有使用volatile,则最后结果不会是20000

    public void addT060(){
        this.number =60;
    }
    public void addPlusPlus(){
        number++;
    }

    AtomicInteger atomicInteger = new AtomicInteger();
    public void addMyAtomic(){
        atomicInteger.getAndIncrement();
    }

}

如何解决?
1、加synchonized关键字;
分析:过重,不适合。
2、直接使用juc下AtomicInteger。
atomicInteger.getAndIncrement(); 解决了多线程环境下number++的问题;底层原理是CAS。
CAS原理:CAS原理描述

三、哪里有用过volatile?

单例模式DCL(Double Check Lock双端检锁机制),DCL机制不一定线程安全,原因是指令重排序的存在,加入volatile可以禁止指令重排。
原因在于某一个线程执行到第一次检测,读取到的instance不为null时,instance的引用对象可能没有完成初始化。
Instance = new SingletonDemo();可以分为以下三步完成。

Memory = allocate(); //1.分配对象内存空间
Instance(memory); //2.初始化对象
Instance = memory;//3.设置instance指向刚分配的内存地址,此时instance!=null

2和3不存在依赖关系,而且无论重排前还是重排后程序的执行结果在单线程中并没有改变。

适用场景

仅当volatile变量能简化代码的实现以及对同步策略的验证时,才应该使用它们。如果在验证正确性时需要对可见性进行复杂的判断,那么就不要使用volatile变量。
例如:检查某个状态标记以判断是否退出循环。

volatile boolean asleep;
while(!asleep){
	countSomeSheep();
} 

满足以下所有条件,才应该使用volatile变量:

  1. 对变量的写入操作不依赖变量的当前值,或者确保只有一个线程更新变量的值
  2. 该变量不会与其他状态变量一起纳入不变性条件中
  3. 在访问变量时不需要加锁

局限性

volatile不能保证递增操作(count++)的原子性,即不可保证原子性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值