JAVA关键字Volatile

本文详细解析了Java中的Volatile关键字,介绍了其在内存模型中的作用,展示了为何它不能完全解决线程安全问题,并探讨了其如何确保可见性但不保证一致性的原理。通过实例演示,揭示了Volatile在多线程环境中的局限性。
摘要由CSDN通过智能技术生成

JAVA关键字Volatile

 

Volatile是在java面试中出现频率很高的一个技术点,说到Volatile不得不先说一下JVM的内存模型,java的内存模型分为主内存和工作内存,

 主内存:

            所有的变量都存储在主内存(不包括局部变量和,方法参数,他们是线程私有的)

 工作内存:

            jvm为每个线程分配的内存空间,工作内存保存了被该内存使用的变量的的主内存副本,

            线程对变量的所有操作(读取、赋值等)都必需在工作内存中进行,而不能直接读写主内存中的变量,不同的线程之间也无法直接访问对方工作内存中的变量,线程之间变量值的传递均需要通过主内存如下图

 

 

         工作内存中变量变化的过程

 

        read:从主内存读取,发生在主内存

        load:加载到工作内存,发生在工作内存

        use:使用,发生在工作内存

        assign:当前线程修改变量

        store:将变量存储在工作内存

        write:将新的值写到主内存

 

可以看到在这样的过程中,如果有多个线程同时修改共享变量做修改的话是会发生线程不安全的,我们写一段代码来看看是不是线程不安全

                       

  int index = 0;
     @Test
    public void unSaveUpadate() throws InterruptedException {
    for (int i = 0; i < 1000; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1L);
                    index++;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
    Thread.sleep(1000L);
    System.out.println("输出值:" + index);

 

                     你会发现最后输出的数据始终是小于1000,造成结果不是预期的原因就是因为多个线程并发修改相同的变量时发生了覆盖的现象,举例:

                    线程1:从主内存中读取变量index = 1; 线程2:从主内存中读取变量index=1;

                   线程1:将index++,将修改的结果2write到主内存,同时线程2也做了同样的动作,将结果2write回主内存

                   实际期望值:3,两个线程操作完结果:2

 

以上便是线程不安全出现的问题。

 

那volatile线程安全吗,验证一下:

   volatile int index = 0;
     @Test
    public void unSaveUpadate() throws InterruptedException {
    for (int i = 0; i < 1000; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1L);
                    index++;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
    Thread.sleep(1000L);
    System.out.println("输出值:" + index);

}

                   结果显示也不安全,实际数据始终是小于1000.

那问题来了,volatile到底解决了什么问题,看看volatile的描述:

1:保证被修饰的变量对所有线程的可见性,值当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的。

 2:禁止指令重排

以上描述可以看出volatile保证了被volatile修饰的变量保证可见性,但是没有描述保证一致性

volatile修饰的变量每次读之前会插入读内存屏障,读之前一定要从主内存拉取新的值,volatile修饰的变量写入工作内存之后,加写屏障,会将其他线程改变量的状态变成失效状态,其他线程读时一定要去读取最新的值

通过上述方法保证了可见性。

 

但是:

1:读取后

2:修改

3:写之前

在1和3之间,其他线程对变来你给做了修改任然会造成不一致的情况。

 

面试的时候有的面试官会问:为什么要做工作内存,都对主内存操作不就没有这个问题了吗?

计算机的内存可以包含主内存,和cup的高速缓存区,我们可以理解对cpu的高速缓存操作要远远快于对主缓存的操作,可以理解为ava为了提交运行速度,java虚拟机栈使用和操作的是cpu的高速缓存,

而主内存使用的是计算的主内存。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值