谈谈你对volatile的理解

您所在的位置:网站首页 美国房价也是只涨不跌吗知乎 谈谈你对volatile的理解

谈谈你对volatile的理解

2023-05-25 09:25| 来源: 网络整理| 查看: 265

一、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读插入内存屏障: 在这里插入图片描述



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3