谈谈你对volatile的理解

一、volatile特性:

volatile是Java虚拟机提供的轻量级的同步机制。主要有三大特性:
保证可见性
不保证原子性
禁止指令重排序

1、保证可见性
1)代码演示
AAA线程修改变量number的值为60,main线程获取到的number值是0,就一直循环等待。
原因:int number = 0;number变量之前没有添加volatile关键字,没有可见性。添加volatile关键字,可以解决可见性问题。

public class VolatileDemo {
    int number = 0;
 
    public void addTo60() {
        this.number = 60;
    }
 
    //volatile可以保证可见性,及时通知其他线程,主物理内存的值已经被修改
    public static void main(String[] args) {
        VolatileDemo volatileDemo = new VolatileDemo();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " come in");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            volatileDemo.addTo60();
            System.out.println(Thread.currentThread().getName() + " update number value:" + volatileDemo.number);
 
        }, "AAA").start();
 
        //第2个线程是main线程
        while (volatileDemo.number == 0) {
            //main线程就一直等待循环,直到number的值不等于0
 
        }
        System.out.println(Thread.currentThread().getName() + " mission is over, main thread number value:" + volatileDemo.number);
    }
}  

2)volatile是如何来保证可见性的呢?
如果对声明了volatile的变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令。
将这个变量所在缓存行的数据写回到系统内存。
这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。

2、不保证原子性
1)代码演示
volatile修饰number,进行number++操作,每次执行number的返回结果都不一样

public class VolatileDemo {
    volatile int number = 0;
 
    public void increase() {
        number++;
    }
 
    public static void main(String[] args) {
        VolatileDemo volatileDemo = new VolatileDemo();
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    volatileDemo.increase();
                }
            }).start();
        }
 
        // 默认有 main 线程和 gc 线程
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + " finally number value:" + volatileDemo.number);
    }
}

2)volatile为什么不保证原子性?
number++被拆分成3个指令:
getfield 从主内存中拿到原始值
iadd 在线程工作内存中进行加1操作
putfield 把累加后的值写回主内存
如果第二个线程在第一个线程读取旧值和写回新值期间读取number的值,
那么第二个线程就会与第一个线程看到同一个值,并执行相同值的加1操作,这也就造成了线程安全失败。
在这里插入图片描述
3)如何解决原子性问题
CAS机制:AtomicInteger number = AtomicInteger(0)
锁机制:synchronized、Lock

3、禁止指令重排序
volatile的写-读与锁的释放-获取有相同的内存效果。
volatile写-读的内存语义:
当写一个volatile变量时,JMM会把线程A对应的本地内存中的共享变量值刷新到主内存。
当读一个volatile变量时,JMM会把线程B对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。
线程A写一个volatile变量,随后线程B读这个volatile变量,实质上是线程A通过主内存向线程B发送消息。

public class VolatileExample {
    int a = 0;
    volatile boolean flag = false;
    public void writer() {
        a = 1;
        flag = true;
    }
    public void reader() {
        if (flag) {
            System.out.println("resultValue:" + a);
        }
    }
}

为了实现volatile的内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序
volatile写插入内存屏障:
在这里插入图片描述
volatile读插入内存屏障:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值