Java并发编程-volatile关键字介绍

简介:

前言

要学习好Java的多线程,就一定得对volatile关键字的作用机制了熟于胸。最近博主看了大量关于volatile的相关博客,对其有了一点初步的理解和认识,下面通过自己的话叙述整理一遍。

有什么用?

volatile主要对所修饰的变量提供两个功能

  • 可见性
  • 防止指令重排序


本篇博客主要对volatile可见性进行探讨,以后发表关于指令重排序的博文。

什么是可见性?

一图胜千言
JAVA内存模型
上图已经把JAVA内存模型(JMM)展示得很详细了,简单概括一下

  1. 每个Thread有一个属于自己的工作内存(可以理解为每个厨师有一个属于自己的铁锅)
  2. 所有Thread共用一个主内存(餐厅所有的厨师共用同一个冰箱)
  3. 每个Thread操作数据之前都会去主内存中获取数据(厨师炒菜之前都要去冰箱里拿食材)
  • Thread:厨师
  • 工作内存:铁锅
  • store&load:放熟食,取食材
  • 主内存:冰箱

读者可思考以下情景:

餐厅来了一位顾客点了一份红烧肉,此时有两位大厨(假设大厨之间互不通信),由于互不通信,所以两位大厨都打开冰箱取出食材开始炒菜。
最后炒出了两份红烧肉,顾客只要一份。为什么会造成这种结果?

由于大厨之间没有可见性。

将此情景放在JAVA中即是:

线程A从主内存中取了一个变量到工作内存中,操作完毕后没有及时放回主内存中,于是线程B去取这个变量已经过期了,取的是线程A操作之前的变量。

如何拥有可见性?

先介绍一下Java内存模型中定义的8种工作内存与主内存之间的原子操作

  • lock( 锁定 ):作用于主内存的变量,把一个变量标识为一条线程独占的状态。
  • unlock(解锁):作用于主内存的变量,把一个处于锁定的变量释放出来,释放变量才可以被其他线程锁定。
  • read(读取):作用于主内存的变量,把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。
  • load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
  • use(使用):作用于工作内存种的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作。
  • assign(赋值):作用于工作内存中的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  • store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用
  • write(写入):作用于主内存的变量,它把store操作从工作内存中得到的值放入主内存的变量中。
读取赋值一个普通变量的情况

普通变量
当线程1对主内存对象发起read操作到write操作套流程的时间里,线程2随时都有可能对这个主内存对象发起第二套操作

  • 有什么危害呢?

假设主内存中有一个

int a=0;

线程1和线程2分别执行一次,理想状态下最终a的值为2.

a++;

线程1在执行了assign操作之后变量a的真实值已经从0变成了1,但是这个过程发生在工作内存中对其他线程不可见,若线程2此时对变量a的操作,读取到的值仍然为0,因为没有可见性,线程2的操作也仅仅是重复了线程1的操作,再次让a从0变成了1。并没有达到期望的a=2。

读取赋值一个volatile变量的情况

volatile变量
volatile变量对对象的操作更严格:

  • use之前不能被read&load
  • assign之后必须紧跟store&write

也就是说 read-load-useassign-store-write成为了两个不可分割的原子操作

尽管这时候在use和assign之间依然有一段真空期,有可能变量会被其他线程读取,但是无论在哪一个时间点主内存的变量和任一工作内存的变量的值都是相等的。这个特性就导致了volatile变量不适合参与到依赖当前值的运算,如自增。
那么依靠可见性的特点volatile可以用在哪些地方呢?
《Java虚拟机》提到:

运算结果并不依赖变量的当前值(即结果对产生中间结果不依赖),或者能够确保只有单一的线程修改变量的值

通常volatile用做保存某个状态的boolean值。


部分参考自

目录
相关文章
|
3月前
|
IDE Java 编译器
java编程最基础学习
Java入门需掌握:环境搭建、基础语法、面向对象、数组集合与异常处理。通过实践编写简单程序,逐步深入学习,打牢编程基础。
240 1
|
3月前
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
186 6
|
4月前
|
SQL Java 数据库
2025 年 Java 从零基础小白到编程高手的详细学习路线攻略
2025年Java学习路线涵盖基础语法、面向对象、数据库、JavaWeb、Spring全家桶、分布式、云原生与高并发技术,结合实战项目与源码分析,助力零基础学员系统掌握Java开发技能,从入门到精通,全面提升竞争力,顺利进阶编程高手。
776 1
|
3月前
|
安全 前端开发 Java
从反射到方法句柄:深入探索Java动态编程的终极解决方案
从反射到方法句柄,Java 动态编程不断演进。方法句柄以强类型、低开销、易优化的特性,解决反射性能差、类型弱、安全性低等问题,结合 `invokedynamic` 成为支撑 Lambda 与动态语言的终极方案。
179 0
|
4月前
|
Java 开发者
Java并发编程:CountDownLatch实战解析
Java并发编程:CountDownLatch实战解析
480 100
|
存储 安全 Java
Java面试题:深入探索Java内存模型,Java内存模型中的主内存与工作内存的概念,Java内存模型中的happens-before关系,volatile关键字在Java内存模型中的作用
Java面试题:深入探索Java内存模型,Java内存模型中的主内存与工作内存的概念,Java内存模型中的happens-before关系,volatile关键字在Java内存模型中的作用
111 1
|
存储 SQL 缓存
揭秘Java并发核心:深度剖析Java内存模型(JMM)与Volatile关键字的魔法底层,让你的多线程应用无懈可击
【8月更文挑战第4天】Java内存模型(JMM)是Java并发的核心,定义了多线程环境中变量的访问规则,确保原子性、可见性和有序性。JMM区分了主内存与工作内存,以提高性能但可能引入可见性问题。Volatile关键字确保变量的可见性和有序性,其作用于读写操作中插入内存屏障,避免缓存一致性问题。例如,在DCL单例模式中使用Volatile确保实例化过程的可见性。Volatile依赖内存屏障和缓存一致性协议,但不保证原子性,需与其他同步机制配合使用以构建安全的并发程序。
265 0
|
存储 缓存 Java
Java 并发编程——volatile 关键字解析
本文介绍了Java线程中的`volatile`关键字及其与`synchronized`锁的区别。`volatile`保证了变量的可见性和一定的有序性,但不能保证原子性。它通过内存屏障实现,避免指令重排序,确保线程间数据一致。相比`synchronized`,`volatile`性能更优,适用于简单状态标记和某些特定场景,如单例模式中的双重检查锁定。文中还解释了Java内存模型的基本概念,包括主内存、工作内存及并发编程中的原子性、可见性和有序性。
343 5
Java 并发编程——volatile 关键字解析
|
缓存 安全 算法
Java面试题:如何通过JVM参数调整GC行为以优化应用性能?如何使用synchronized和volatile关键字解决并发问题?如何使用ConcurrentHashMap实现线程安全的缓存?
Java面试题:如何通过JVM参数调整GC行为以优化应用性能?如何使用synchronized和volatile关键字解决并发问题?如何使用ConcurrentHashMap实现线程安全的缓存?
196 0
|
缓存 安全 Java
Java volatile关键字:你真的懂了吗?
`volatile` 是 Java 中的轻量级同步机制,主要用于保证多线程环境下共享变量的可见性和防止指令重排。它确保一个线程对 `volatile` 变量的修改能立即被其他线程看到,但不能保证原子性。典型应用场景包括状态标记、双重检查锁定和安全发布对象等。`volatile` 适用于布尔型、字节型等简单类型及引用类型,不适用于 `long` 和 `double` 类型。与 `synchronized` 不同,`volatile` 不提供互斥性,因此在需要互斥的场景下不能替代 `synchronized`。
3474 3