1.volatile关键字及其作用
2.synchronized和volatile区别
3.深入理解和使用volatile关键字
volatile关键字及其作用
本文主要介绍Java语言中的volatile关键字,内容涵盖volatile的保证内存可见性、禁止指令重排等。
1 保证内存可见性
1.1 基本概念
可见性是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是face架构源码一个线程修改的结果,另一个线程马上就能看到。
1.2 实现原理
当对非volatile变量进行读写的时候,每个线程先从主内存拷贝变量到CPU缓存中,如果计算机有多个CPU,每个线程可能在不同的CPU上被处理,这意味着每个线程可以拷贝到不同的CPU cache中。
volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,保证了每次读写变量都从主内存中读,跳过CPU cache这一步。当一个线程修改了这个变量的传奇源码交易值,新值对于其他线程是立即得知的。
2 禁止指令重排
2.1 基本概念
指令重排序是JVM为了优化指令、提高程序运行效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度。指令重排序包括编译器重排序和运行时重排序。
在JDK1.5之后,可以使用volatile变量禁止指令重排序。针对volatile修饰的变量,在读写操作指令前后会插入内存屏障,指令重排序时不能把后面的指令重排序到内存屏障后面。
2.2 指令重排带来的问题
如果一个操作不是原子的,就会给JVM留下重排的机会。
如果线程A中的指令发生了重排序,那么B中很可能就会拿到一个尚未初始化或尚未初始化完成的netty源码系列context,从而引发程序错误。
2.3 禁止指令重排的原理
volatile关键字提供内存屏障的方式来防止指令被重排,编译器在生成字节码文件时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。
JVM内存屏障插入策略:
每个volatile写操作的前面插入一个StoreStore屏障;
在每个volatile写操作的后面插入一个StoreLoad屏障;
在每个volatile读操作的后面插入一个LoadLoad屏障;
在每个volatile读操作的后面插入一个LoadStore屏障。
2.4 指令重排在双重锁定单例模式中的影响
基于双重检验的单例模式(懒汉型)
instance= new Singleton()并不是一个原子操作,其实际上可以抽象为下面几条JVM指令:
上面操作2依赖于操作1,但是操作3并不依赖于操作2。所以JVM是可以针对它们进行指令的优化重排序的,经过重排序后如下:
指令重排之后,instance指向分配好的内存放在了前面,而这段内存的初始化被排在了后面。在线程A执行这段赋值语句,在初始化分配对象之前就已经将其赋值给instance引用,恰好另一个线程进入方法判断instance引用不为null,然后就将其返回使用,源码安装saltstack导致出错。
解决办法
用volatile关键字修饰instance变量,使得instance在读、写操作前后都会插入内存屏障,避免重排序。
3 适用场景
(1) volatile是轻量级同步机制。在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,是一种比synchronized关键字更轻量级的同步机制。
(2) volatile**无法同时保证内存可见性和原子性。加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性**。
(3) volatile不能修饰写入操作依赖当前值的变量。声明为volatile的简单变量如果当前值与该变量以前的值相关,那么volatile关键字不起作用,易保全源码也就是说如下的表达式都不是原子操作:“count++”、“count = count+1”。
(4) 当要访问的变量已在synchronized代码块中,或者为常量时,没必要使用volatile;
(5) volatile屏蔽掉了JVM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。
想获取更多知识和学习资料可以扫描下方二维码或是点击链接
mp.weixin.qq.com/s/vkvY...
synchronized和volatile区别
synchronized和volatile区别:
volatile本质:是java虚拟机(JVM)当前变量在工作内存中的值是不确定的,需要从主内存中读取;synchronized则是锁定当前的变量,只有当前线程可以访问到该变量,其他的线程将会被阻塞。
扩展资料
volatile只能实现变量的修改可见性,并不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性。
volatile只能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的。
volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
深入理解和使用volatile关键字
大家好!今天小黑要和大家聊聊Java并发编程的一个重要话题——volatile关键字。在Java的世界里,掌握并发编程是一项必备技能,尤其是当咱们处理多线程应用时。你可能听说过这样的情况:即使你的代码看起来毫无问题,但在并发环境下,它们就像是刚从床上起来的头发,乱七八糟!为什么会这样呢?原因在于多线程操作时存在的一些难以察觉的陷阱,比如变量的可见性问题、操作的原子性问题等等。
Java提供了多种机制来处理这些问题,其中volatile关键字就是一个重要的工具。可能有人会问,这个volatile到底是个什么东西?简单来说,它是Java提供的一种轻量级的同步机制。但别小看了这个“轻量级”,它在确保变量在多线程环境下的可见性方面,可是有着不可小觑的作用。在接下来的内容中,小黑将带你深入了解volatile,以及它在Java并发编程中的应用和局限性。
好了,现在咱们来深入了解一下volatile这个“神秘”的关键字。在Java中,volatile是一种用于声明变量的修饰符。它告诉JVM和编译器,这个变量可能会被多个线程同时访问,而且还不通过锁来控制。这听起来有点像是给变量加了一个“注意”标签,让它在并发环境下表现得更好。
首先,小黑给大家强调一下,volatile主要解决的是可见性问题。可见性,就像它字面上的意思,确保当一个线程修改了volatile变量的值时,其他线程能够立即知道这个改变。这听起来很简单,但在并发编程中,这个特性非常重要。为什么呢?因为在多线程环境中,每个线程可能在自己的工作内存中保留了变量的副本,这就导致了一个线程对变量的修改,其他线程不一定能立即看到。
下面小黑用一个小例子来展示volatile的使用。假设有一个简单的场景,我们有一个标志位变量,控制着一个线程的运行状态:
在这个例子中,flag变量被声明为volatile。这意味着,当stopThread方法被调用,将flag设置为true时,正在运行的线程会立即看到这个改变,并退出while循环。
volatile是Java并发编程中一个非常有用的工具,尤其是在处理可见性问题时。但是它并不是万能的,有它的局限性。
最后,通过这些例子,咱们可以看到volatile在实际编程中的应用场景。它是一个强大的工具,但要记住它的局限性和合适的使用场景。咱们在编写并发程序时,应该根据具体需求选择合适的同步机制。