java volatile关键字的作用_Java并发编程彻底搞懂volatile关键字「建议收藏」

2022-08-23 14:38:46 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

背景

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关键字解决的是多个线程之间访问资源的同步性。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/138498.html原文链接:https://javaforall.cn

0 人点赞