什么是JMM以及volatile的特性(对于volatile的理解)

JMM是java内存模型

在这里插入图片描述
JMM有三大特性:可见性,原子性,有序性
volatile 三大特性:它是java虚拟机提供的轻量级的同步机制,有三个特点:1 保证可见性 2:不保证原子性 3:禁止指令重排
在这里插入图片描述

在这里插入图片描述
t1在自己的工作内存中改了age的数值后又传回给主物理内存,但是其他两个线程并不知情
在这里插入图片描述
此时涉及到JMM中的一个特性,可见性,就是一个线程修改主物理内存后其他线程得到通知,重新更新那个变量
代码证明:
在这里插入图片描述
在这里插入图片描述
程序没有停,因为main线程没有收到number修改的信息

修改后,加入了volatile关键字以后,一个线程修改了变量,其他线程也会收到信息
在这里插入图片描述

volatile不保证 原子性

运行结果不是20000
在这里插入图片描述
在这里插入图片描述

如何解决原子性

在这里插入图片描述
在这里插入图片描述
可以看到下边这条正常执行了
在这里插入图片描述

volatile禁止指定重排

指令重排
在这里插入图片描述
在这里插入图片描述
如图,语句4因为存在数据的依赖性,因为他不确定到底是用11还是16所以这里不会指令重排。
在这里插入图片描述
再看另一个指令重排的案例:
在这里插入图片描述
单线程环境下按顺序执行不需要考虑,多线程环境下就有可能发生指令重排,例如:flag=true抢在了a=1的前边,此时另一个线程抢到了时间片执行if方法,那么a的值就为5.
volatile实现禁止指令重排优化,从而避免多线程环境下程序出现乱序执行的现象。

为什么volatile能实现禁止指令重排优化呢

内存屏障(Memory Barrier)又称内存栅栏,是一个CPU指令它的作用有两个: 一是保证特定操作的执行顺序,二是保证某些变量的内存可见性(奎勇该特性实现volatile内存可见性)
由于编译器和处理器都能执行指令重排优化。如果在指令间插入一条Memory Barrier则会告诉编译器和CPU,不管什么指令都不能和这条Memory Barrier指令重排序,也就是说通过插入内存屏障禁止在内存屏障前后的指令执行重排序优化。内存屏障另外一个作用是强制刷出各种CPU的缓存数据,因此任何CPU上的线程都能读取到这些数据的最新版本。
在这里插入图片描述
在这里插入图片描述

在哪些地方用过volatile

单例模式 :
如果只被new了一次的话,那么这句话就会被打印一次。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
上边这个实例是正常情况下单线程执行的情况
在多线程环境下单例模式很显然出问题了
在这里插入图片描述
加了关键字synchronized关键字以后这个控制住了
在这里插入图片描述
用DCL解决
在这里插入图片描述
这里只是用了一个同步代码块,并没有把整个方法锁起来,相比前一种方法更好一点。
即使是这个方法也有一个潜在的隐患,因为多线程环境下系统会进行指令重排,所以有可能会出现错误,因此加入volatile可以禁止指令重排
在这里插入图片描述
但是指令重排只会保证串行语义的执行的一致性(单线程),但并不会关心多线程间的语义一致性,所以当一条线程访问instance不为null时,由于instance实例未必已初始化完成,也就造成了线程安全问题。
下图代码
在这里插入图片描述
地址空间分配出去了,但是在初始化的过程中可能有其他线程读到这里,发现对象为空,所以为了解决这种线程不安全的问题需要加入volatile关键字禁止指令重排
在这里插入图片描述

Supongo que te gusta

Origin blog.csdn.net/delete_bug/article/details/119120918
Recomendado
Clasificación