Java Volatile关键字你真的了解吗

 更新时间:2021年12月21日 17:17:32   作者:mikechen的互联网架构  
这篇文章主要为大家介绍了Java Volatile关键字,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助

正文

在谈 Volatile 之前,我们先回顾下 Java 内存模型 的三要素:原子性、可见性、有序性,也就是大家常提到的并发编程三要素。

并发编程的三要素

1.原子性

和数据库事务中的原子性一样,满足原子性特性的操作是不可中断的,要么全部执行成功要么全部执行失败。

只有简单的读取、赋值(而且必须是将数字赋值给某个变量,变量之间的相互赋值不是原子操作)才是原子操作。

比如:

i = 2;j = i;i++;i = i + 1;

上面4个操作中,i=2是读取操作,必定是原子性操作,j=i你以为是原子性操作,其实吧,分为两步,一是读取i的值,然后再赋值给j,这就是2步操作了,称不上原子操作,i++和i = i + 1其实是等效的,读取i的值,加1,再写回主存,那就是3步操作了。

所以上面的举例中,最后的值可能出现多种情况,就是因为满足不了原子性。

非原子操作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作,java的concurrent包下提供了一些原子类:比如:AtomicIntegerAtomicLong等。

2.可见性

多个线程访问同一个共享变量时,其中一个线程对这个共享变量值的修改,其他线程能够立刻获得修改以后的值。

3.有序性

编译器和处理器为了优化程序性能而对指令序列进行重排序,也就是你编写的代码顺序和最终执行的指令顺序是不一致的。但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。

Volatile

Volatile 是一个Java语言的类型修饰符,一旦一个共享变量(类的成员变量、类的静态成员变量)被Volatile修饰之后,那么就具备了两层语义:

1、保证多线程下的可见性

2、禁止进行指令重排序(即保证有序性)

这里需要注意一个问题,Volatile 只能让被他修饰内容具有可见性、有序性 。Volatile只能保证对单次读/写的原子性,i++ 这种操作不能保证原子性。

Volatile 的内存模型

Volatile 的内存模型

**Java 内存模型(JMM)**是一种抽象的概念,并不真实存在,它描述了一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段、静态字段和构成数组对象的元素)的访问方式。

试图屏蔽各种硬件和操作系统的内存访问差异,以实现让 Java 程序在各种平台下都能达到一致的内存访问效果。

Java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。

  • 主内存 主要存储的是Java实例对象,所有线程创建的实例对象都存放在主内存中,不管该实例对象是成员变量还是方法中的本地变量(也称局部变量),当然也包括了共享的类信息、常量、静态变量。由于是共享数据区域,多条线程对同一个变量进行访问可能会发现线程安全问题。
  • 工作内存 每条线程都有自己的工作内存(Working Memory,又称本地内存,可与前面介绍的处理器高速缓存类比),线程的工作内存中保存了该线程使用到的变量的主内存中的共享变量的副本拷贝。

工作内存是 JMM 的一个抽象概念,并不真实存在 。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

主要存储当前方法的所有本地变量信息(工作内存中存储着主内存中的变量副本拷贝),每个线程只能访问自己的工作内存,即线程中的本地变量对其它线程是不可见的,就算是两个线程执行的是同一段代码,它们也会各自在自己的工作内存中创建属于当前线程的本地变量,当然也包括了字节码行号指示器、相关Native方法的信息。

Volatile 的实现原理

Volatile 保证内存可见性

Volatile 的实现原理

主内存和工作内存之间的交互有具体的交互协议,JMM定义了 八种操作 来完成,这八种操作是 原子的 、 不可再分的 ,它们分别是:lockunlockreadloaduseassignstorewrite

其中,lock , unlock , read , write 作用于主内存; load ,use , assign , store 作用于工作内存。

(1) lock

将主内存中的变量锁定,为一个线程所独占。

(2) unclock

将lock加的锁定解除,此时其它的线程可以有机会访问此变量。

(3) read

将主内存中的变量值读到工作内存当中。

(4) load

将read读取的值保存到工作内存中的变量副本中。

(5) use

将值传递给线程的代码执行引擎。

(6) assign

将执行引擎处理返回的值重新赋值给变量副本。

(7) store

将变量副本的值存储到主内存中。

(8) write

将 store 存储的值写入到主内存的共享变量当中。

  • 从主存复制变量到当前工作内存(read and load)
  • 执行代码,改变共享变量值 (use and assign)
  • 用工作内存数据刷新主存相关内容 (store and write)

指令规则

  • read 和 load、store 和 write 必须成对出现。
  • assign 操作,工作内存变量改变后必须刷回主内存。
  • 同一时间只能运行一个线程对变量进行 lock,当前线程 lock 可重入,unlock 次数必须等于 lock 的次数,该变量才能解锁。
  • 对一个变量 lock 后,会清空该线程工作内存变量的值,重新执行 load 或者 assign 操作初始化工作内存中变量的值。
  • unlock 前,必须将变量同步到主内存( store/write 操作)。

Volatile源码案例

Volatile源码案例

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!

相关文章

  • RabbitMQ消息确认机制剖析

    RabbitMQ消息确认机制剖析

    这篇文章主要为大家介绍了RabbitMQ消息确认机制剖析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • slf4j与log4j全面了解

    slf4j与log4j全面了解

    下面小编就为大家带来一篇slf4j与log4j全面了解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-07-07
  • Swagger的使用教程详解

    Swagger的使用教程详解

    Swagger是一个强大的API文档工具,它能够简化API文档的编写和维护工作,提供了一种方便的方式来描述、展示和测试RESTful风格的Web服务接口,本文介绍了Swagger的安装配置和使用方法,并提供了示例代码,感兴趣的朋友一起学习吧
    2023-06-06
  • 使用SpringEvent解决WebUploader大文件上传解耦问题

    使用SpringEvent解决WebUploader大文件上传解耦问题

    Spring Event是Spring框架内建的一种发布/订阅模式的实现,它允许应用内部不同组件之间通过事件进行通信,本文以WebUploader大文件上传组件为例,在大文件处理的场景中使用SpringEvent的事件发布机制,灵活的扩展对文件的处理需求,需要的朋友可以参考下
    2024-07-07
  • 你真的了解Java的多线程方法吗

    你真的了解Java的多线程方法吗

    这篇文章主要为大家详细介绍了Java的多线程方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • 关于log4j2的异步日志输出方式

    关于log4j2的异步日志输出方式

    这篇文章主要介绍了关于log4j2的异步日志输出方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Spring Boot中优雅地处理参数传递的技巧分享

    Spring Boot中优雅地处理参数传递的技巧分享

    最近一直在学习Spring Boot,今天将其中的从前台过来的参数传递总结一下,下面这篇文章主要给大家介绍了关于Spring Boot中优雅地处理参数传递的技巧,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-05-05
  • Java中tomcat memecached session 共享同步问题的解决办法

    Java中tomcat memecached session 共享同步问题的解决办法

    这篇文章主要介绍了Java中tomcat memecached session 共享同步问题的解决办法的相关资料,需要的朋友可以参考下
    2015-10-10
  • java 将一个数组逆序输出的方法

    java 将一个数组逆序输出的方法

    今天小编就为大家分享一篇java 将一个数组逆序输出的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-06-06
  • mybatis多表查询的实现(xml方式)

    mybatis多表查询的实现(xml方式)

    本文主要介绍了mybatis多表查询的实现(xml方式),文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03

最新评论