一定要你明白Java中的volatile

今天Tony来和大家聊聊Java中关键字volatile。

字节码

首先volatile int a = 3;和int a = 3;,
加不加volatile关键字,最终生成的字节码都一样的。有兴趣的同学可以试试看看字节码是否一样。

英文解释

Adding volatile to the field does not change Java bytecode that reads or writes the field. It only changes the interpretation of the program by JVM or JIT compilation output if needed. It also influences optimizations.

中文理解

内存屏障的概念是针对CPU架构级别的,需要在JIT编译器生成机器码的时候才能看到。

java内存

讲讲java内存,在java内存中所有的变量都存在与主内存中,每个线程都有自己的工作内存。每个线程中的所用到的变量都是一个副本,都是从主内存中拷贝过来的。

在多线程的场景下,处于性能的考虑。每个线程处理变量的时候都会从主内存复制到当前的cpu缓存中,因为cpu缓存处理速度是相当的快。随之带来的问题就是一个变量被多个线程在不同的cpu中访问。

不同线程之间不能直接访问对方线程间的变量。他们之间的数据都传递都是通过主内存来实现的。

volatile关键字

java中用volatile 修饰的关键字将会标记在“主内存”中,每次对volatile变量的读取都是从计算机的主内存读取,不是从cpu缓存中读取。可以这么理解每次我都是取主内存里的数据。

同样对volatile变量的写入同时会写入到主内存和cpu缓存中。


不用volatile

举个例子,当线程T1对变量a进行写操作此刻a=1,如果此刻没有使用
volatile 关键字,那么此刻线程T2中的a还是等于0。
这个时候线程T2并不能读取到线程T1写的a=1,此刻线程T1并没有将a=1回写到主内存中去。

线程的可见性,就是上面说的这种情况。1个线程对变量进行操作并不会通知到另一个线程。

使用volatile内存可见性

publicclassVolatileObject{publicvolatileinta=0;}

上面代码当线程T1修改a的值,另一个线程T2读取a的自值,
在上面给出的场景中,一个线程(T1)修改计数器,另一个线程(T2)读取计数器(但从不修改),声明计数器变量volatile足以保证T2对计数器变量的写入可见。

指令重排序

1、重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。

2、调制执行语句的顺序,重排序是为了更好的提升性能。

3、重排序保证在单线程下不会改变执行结果,但在多线程下可能会改变执行结果。

举个例子

intx=1;//(1)inty=2;//(2)intz=x+y;//(3)
  • JVM在单线程中不影响结果的情况下会对指令重排序

在上面的代码中,先执行(1)(2)再执行(3)和先执行(2)(1)(3)对你最终结果都没有什么影响,在JVM中居于优化有可能执行的是
(2)(1)(3)

Happens-Before

Happens-Before原则

happens-before原则是Java内存模型中定义的两项操作之间的偏序关系,如果说操作A先行发生于操作B,其实就是说在发生操作B之前,操作A产生的影响能被操作B观察到。“影响”包括修改了内存中共享变量的值、 发送了消息、 调用了方法等。

volatile机制

volatile 并不能保证原子性
想象一下,如果线程T1将一个值为0的共享计数器变量读入其CPU缓存,则将其递增为1,并没有将更改后的值写回主内存。

然后线程T2可以将相同的计数器变量从主内存(此刻变量值仍然为0)读取到自己的CPU缓存中。然后线程2也可以将计数器增加到1,并且也不会将其写回主内存。这种情况如下图所示

这个时候并不能保证原子性的效果,线程T2并没有读取到最近的数据。

volatile的底层是使用内存屏障来保证有序性的。

内存屏障有两个能力:

  • 就像一套栅栏分割前后的代码,阻止栅栏前后的没有数据依赖性的代码进行指令重排序,保证程序在一定程度上的有序性。
  • 强制把写缓冲区/高速缓存中的脏数据等写回主内存,让缓存中相应的数据失效,保证数据的可见性。

总结

  • 每个线程操作的都是自己工作内存中的数据,并发的情况下线程
    T1并没有将最新修改的数据刷新到主内存中去,所以线程T2无法读取到最新的数据。
  • volatile 不能保证原子性,不能保证原子性。
  • volatile 关键字修饰的代码不会被重排序。
  • volatile 会在指令序列中插入内存屏障来禁止重排序。

题外话

最近市场环境不好,裁员的比比皆是。这是一个优胜劣汰的时代,那些缺乏竞争力的企业只会走向死亡,而员工缺乏自己的核心竞争力也无法在内部立足。所以我们对自己的未来要有方向有规划,大龄码农不能再通过 跳槽溢价涨工资。

原文地址:https://www.cnblogs.com/CQqfjy/p/12286393.html

时间: 2024-10-12 03:25:50

一定要你明白Java中的volatile的相关文章

理解java中的volatile关键字

Java语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量.这两种机制的提出都是为了 实现代码线程的安全性.Java 语言中的 volatile 变量可以被看作是一种 "程度较轻的 synchronized":与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分. volatile 写和读的内存语义: 线程 A 写一个 volatile 变量,实质上是线程 A

Java中的volatile关键字为什么不是不具有原子性

Java中long赋值不是原子操作,因为先写32位,再写后32位,分两步操作,而AtomicLong赋值是原子操作,为什么?为什么volatile能替代简单的锁,却不能保证原子性?这里面涉及volatile,是java中的一个我觉得这个词在Java规范中从未被解释清楚的神奇关键词,在Sun的JDK官方文档是这样形容volatile的: The Java programming language provides a second mechanism, volatile fields, that

java中关键字volatile的作用

用在多线程,同步变量. 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B.只在某些动作时才进行A和B的同步.因此存在A和B不一致的情况.volatile就是用来避免这种情况的.volatile告诉jvm, 它所修饰的变量不保留拷贝,直接访问主内存中的(也就是上面说的A) =========================分割线1================================= 版权声明 :转载时请以超链接形式标明文章原始出处和作者信息及本

Java中的volatile关键字

一.计算机内存模型的相关概念 计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,可能会涉及到数据的读取和写入.由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢得多,因此如果任何时刻对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度,因此自CPU里面就有了高速缓存. 也就是,当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU高速缓存中,那么CPU进行计

java中的Volatile 变量

Java 语言中的 volatile 变量可以被看作是一种 “程度较轻的 synchronized”:与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分.本文介绍了几种有效使用 volatile 变量的模式,并强调了几种不适合使用 volatile 变量的情形. 锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility).互斥即一次 只允许一个线程持

java中的 Volatile

Java 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量.这两种机制的提出都是为了实现代码线程的安全性.其中 Volatile 变量的同步性较差(但有时它更简单并且开销更低),而且其使用也更容易出错. Java 语言中的 volatile 变量可以被看作是一种 “程度较轻的 synchronized”:与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是synchronized 的一部分.本文介绍了几

[读书笔记]java中的volatile关键词

以下内容大多来自周志明的<深入理解Java虚拟机>. 当一个变量被volatile修饰后,它将具备两种特性: 1. 保证此变量对所有线程的可见性,这里的“可见性”是指当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的.而普通变量不能做到这一点,普通变量的值在线程间传递均需要通过主内容来完成,例如,线程A修改了一个普通变量的值,然后向主内容进行回写,另外一条线程B在线程A回写完成之后再从主内存进行读取操作,新变量值才会对线程B可见. volatile变量在各个线程的工作内存中不存

java中的volatile关键字初识

java中volatile关键字的含义 在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉. Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和 volatile 关键字机制. synchronized 同步块大家都比较熟悉,通过 synchronized 关键字来实现,所有加上synchronized 和 块语句,在多线程访问的时候,同一时刻只能有一个线程能够用 synchr

java中的volatile变量

同步与线程间通信: 通信 通信是指消息在两条线程之间传递. 既然要传递消息,那接收线程 和 发送线程之间必须要有个先后关系,此时就需要用到同步.通信和同步是相辅相成的. 同步 同步是指,控制多条线程之间的执行次序. 线程间通信方式: 共享内存 共享内存指的是多条线程共享同一片内存,发送者将消息写入内存,接收者从内存中读取消息,从而实现了消息的传递. 但这种方式有个弊端,即需要程序员来控制线程的同步,即线程的执行次序. 这种方式并没有真正地实现消息传递,只是从结果上来看就像是将消息从一条线程传递到