大家好,又见面了,我是你们的朋友全栈君。
背景
Java线程控制中常用的两个关键字:synchronized、volatile
因上篇文章《程序员眼中的Synchronized同步锁》对synchronized关键字进行来详解。本篇文章主要对volatile关键字进行解剖。
volatile关键字特性
内存可见性(Memory Visibility),所有线程都能看到共享内存的最新状态;有序性;不具备原子性(最致命缺点)。volatile解决什么样的问题?
同步死循环
P ri n t S t ri n g
Run方法
说明:上述代码当程序运行在-server服务器模式中64bit的JVM上时,会出现死循环。解决办法是使用volatile关键字。
关键字volatile的作用是强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量值。
异步死循环在谈异步死循环前,我们先看如下代码:
Ru n T h re a d
Run方法
说明:上述代码当程序运行在-server服务器模式中64bit的JVM上时,同样会出现死循环。
分析为什么会出现这种情况?
在启动RunThread线程时,变量isRunning == true;存在于公共堆栈及线程的私有堆栈中。在JVM被设置为-server模式时为了线程运行的效率,线程一直在私有堆栈中取得isRunning的值时true。而代码thread.setRunning(false);虽然被执行,更新的却是公共堆栈中的isRunning变量的值fals,所以一直就是死循环的状态。内存结构图如下:
线程私有堆栈图
上述问题解决方案其实很简单,跟同步死循环解决方案一致使用volatile关键字,其内存结构如下:
读取公共内存
volatile为什么不具备原子性?一张图看懂变量在内存中的工作流程。
变量在内存中工作流程
read和load阶段,从主存复制变量到当前线程工作内存;use和assign阶段,执行代码,改变共享变量值;store和write阶段,用工作内存数据刷新注册对应变量值。说明:在多线程环境中,use和assign是多次出现的,但这一操作并不是原子性,也就是read和load之后,如果主内存count变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化,也就是私有内存和公共内存中的变量不同步,所以计算时结果和预期不一致,也就出现线程安全问题。
对于volatile修饰的变量,JVM虚拟机只是保证从主内存加载到线程工作内存的值是最新的;因此volatile关键字解决的是变量【读】时的可见性问题,但无法保证原子性,对于多个线程访问同一个实例变量时需要进行【加锁】同步。
总结
volatile和synchronized两者之间比较:
关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好;volatile只能修饰变量,而synchronized可以修饰方法、代码块等。多线程访问volatile不会发生阻塞,而synchronized会出现阻塞。volatile能保证数据的可见性,但不能保证数据的原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步处理。关键字volatile解决的是变量在多个线程之间的可见性;而synchronized关键字解决的是多个线程之间访问资源的同步性。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/138498.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...