volatile是研究Java并发编程绕不过去的一个关键字,先说结论:

volatile的作用:

1.保证被修饰变量的可见性

2.保证程序一定程度上的有序性

3.不能保证原子性

下面,我们将从理论以及实际的案例来逐个解析上面的三个结论

一、可见性

什么是可见性?

举个例子,小明和小红去看电影,刚开始两个人都还没买电影票,小红就先去买了两张电影票,没有告诉小明。小明以为小红没买,所以也去买了两张电影票,因为他们只有两个人,所以他们只能用两张票,这就是小明和小红他俩电影票的数量的可见性。

在讲解之前,我们简单的了解一下JVM当中运行时数据区的结构

堆内存:存放的就是对象,所以它也是JVM当中内存最大的一区域

线程私有区:线程中的栈会去从堆当中获取变量的值来进行操作,正是因为是私有化的,所以两个线程之间的数据是不会共享的

元空间:存放静态变量以及常量还有被虚拟机加载的类信息

同理,我们可以将小明和小红看作java当中的两个线程1和2,共有一个变量

public class volatileTest {public static boolean flag = false;public static void main(String[] args) {try {new Thread(() -> {System.out.println("线程1开始");//线程1当中取反值,当flag为true时才会跳出循环while (!flag) {}System.out.println("线程1结束");}).start();Thread.sleep(100);new Thread(() -> {System.out.println("线程2开始");//线程2给flag赋值flag = true;System.out.println("线程2结束");}).start();} catch (Exception e) {e.printStackTrace();}}
}

该代码的运行结果如下:

可以很清楚的看到,只有线程2是跑完了的,但是明明线程2已经给flag赋值,线程1并没有停止循环,这就是flag这个变量没有可见性,导致线程1一直不停止

解决的方法有两种

第一:让每个线程空余时间就去堆同步数据(显然不合理)

第二:使用volatile关键字去修饰变量flag

让我们加上volatile试试:

这回线程1总算是成功停止了,由此我们可得,volatile是可以让变量具有可见性的。

学习编程不能只知道如何去使用,而是要知道原理,这样才会有更多的薪资

那么volatile的底层是如何实现的呢?

如上面jvm运行数据区的图所示,所有的变量都是存在了堆当中,而每个线程都是拿到他们的副本进行计算和修改,volatile干了啥事呢,如下图所示

这里我们介绍一个新的概念,叫总线(各位可以把它理解成进行连接线程和堆内存,在计算机的硬件当中,也是有总线的,了解的朋友可以把它用相同概念理解一下)。

当一个被volatile修饰的变量,在某一个线程当中被修改时,总线会监听到这个变动,并且会让其他线程中的这个变量失效,简而言之,当线程2当中堆flag进行了修改,则会导致线程1当中的flag失效,就是把这个线程1当中的flag删了。当线程1中没有flag了,它会重新去获取flag,这个时候,就会使我们的变量flag具有了可见性。

现在我们已经知道了,volatile的实行原理,那么它的底层是如何实现的?

众所周知,java语言加载时  -> class  ->汇编语言 -> 机器语言,因为volatile是个关键字,所以它的底层是一种汇编语法,被volatile修饰的变量其实就是给它加了个一个lock前缀指令。

也就是说,当面试官问到我们,如何手写一个volatile时,我们可以说在编译的层面,添加一个lock前缀指令相当于一个内存屏障,它本身会提供三个功能

1)它会强制堆缓存的修改操作立即写入主存

2)如果是写操作,它会导致其他CPU中对应的缓存行无效

3)它会确保指令重排序时不会吧其它的指令排到内存屏障之前的位置,也不会之前的操作拍到内存屏障之后

前面两点很好理解,并且我们也进行了进一步的认证,第三点可能有朋友不太明白,这就引出了我们下一个论点,volatile可以保证一定的有序性

二、有序性

我们看下面三行代码

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

在我们的理解当中,程序时自上而下运行的,先是第一行,再是第二行等,然而事实上,jvm可能会对代码进行重排序,比如它可能就会让上面的这三行代码变成下面的状态

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

为什么会进行重排序,目的是让代码执行的速度更快,当然它也不是随便乱排的,排序的规则是根据代码的依赖性进行的判断,简而言之就是在不影响结果的情况下进行排序,感兴趣的朋友可以自行去了解一下

这是java本身对程序保证的有序性,在不影响运行结果的情况下进行重排序,但是仅限于单线程的情况下,在多线程的情况中,并不能有效地保证程序的有序性

下图为手写的一个单例模式,不做过多的赘述,左边为代码,右边为翻译的字节码文件

通过上图可以很清晰的看出,new OnlyObject这个操作重点分为了四步,

第一步:创建这个对象

第二步:调用这个类的构造方法

第三步:添加指向(就是从私有线程当中执行堆)

第四步:加载

由于java对程序的重排序,会使第二步和第三步进行调换位置,在单线程当中不会有任何问题,而在多线程当中就有问题了

看下图代码

当线程1已经完成添加指向时,在堆当中其实已经分配了一个值,但是这时并没有调用构造方法,所以导致此时这个对象只是一个半成品对象 ,里面并不是我们想要的值。这时线程2走进来,他发现object并不为空,所以直接返回了,此时的程序跟我们的业务并不相符,所以我们需要使用volatile来保证我们的有序性。

以上都是本人的拙见,有错误的地方还请大家帮忙指出,谢谢各位

Java中的关键字volatile解析相关推荐

  1. Java中的关键字--volatile

    volatile关键字经常用来修饰变量.不过,volatile本身很容易被误用.本篇就介绍一下volatile的原理和使用方式. 在介绍volatile关键字原理前,我们首先要了解JVM运行时的内存分 ...

  2. java中static关键字的解析

    静态的特点: A:随着类的加载而加载 B:优先于对象而存在 C:静态是被所有对象共享的数据 这也是我们来判断是否使用静态的标准 D:静态的出现,让我们的调用方式多了一种 类名.静态的内容 非静态的内容 ...

  3. Java中synchronized和volatile有什么区别?

    Java中synchronized和volatile有什么区别? 相关内容 synchronized的问题 什么叫做不完整对象,这个怎么理解呢? 总结 ) 相关内容 1.Java语言为了解决并发编程中 ...

  4. java中的关键字static

    原文链接: https://zhuanlan.zhihu.com/p/70110497 昨晚面试中被问到,没能回答出来.这篇答疑文章写得非常好,无一字可增删,遂直接复制. 在平时开发当中,我们经常会遇 ...

  5. java 死锁 内存消耗_详解Java中synchronized关键字的死锁和内存占用问题

    先看一段synchronized 的详解: synchronized 是 java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并 ...

  6. [转载] Java中的关键字含义以及介绍

    参考链接: C++和Java中static关键字的比较 Java关键字是有特别意义的标识符.用来表示一种数据类型,或者表示程序的结构等. Java关键字不能用作变量名.方法名.类名.包名和参数. 以下 ...

  7. java中的关键字英汉

    java中的关键字 初入java记录贴 类: enum(枚举).interface(接口).class(类).extends(继承).implements(实现接口) 对象: new(创建).inst ...

  8. 【Java_基础】Java中Native关键字的作用

    本篇博文转载与:Java中Native关键字的作用 转载于:https://www.cnblogs.com/leiblog/p/10529056.html

  9. java中final关键字的使用

    final 中文翻译为 最终的,在java中也是较为常用的关键字之一. 在java 中 final 关键字可以修饰  类.方法.变量 final 修饰在类上,则表示该类不能被继承,如果里面的成员变量没 ...

最新文章

  1. linux python2和python3共存_linux-Centos7安装python3并与python2共存
  2. 两款接口测试工具推荐
  3. 存储的瓶颈--大型网站技术演进思考
  4. 大学学习方法介绍之:使用两套教材
  5. 新零售赛道上,便利蜂的美食牌
  6. 2009年5月软件设计师考前预测试题及考点解析
  7. [Leetcode][第1025题][JAVA][除数博弈][数学][递推]
  8. linux php ldap_linux php ldap安装配置的方法
  9. avs3 ts格式封装 标准_超能课堂(204):多媒体容器格式变迁录
  10. 用户权限管理shiro
  11. 【数据结构的魅力】004.堆、前缀树、桶排序、排序算法总结
  12. Elasticsearch6.3.0环境安装
  13. Sigmoid函数简介
  14. 《金字塔原理》要点汇总
  15. css在线代码生成工具汇总
  16. Raspberry 2B+系统安装Hass
  17. 优秀员工评审表 模板
  18. 使用原版镜像安装windows时install.wim文件过大的解决方案
  19. Hive之——数据定义
  20. java throw 什么意思_Java中throw和throws有什么区别?

热门文章

  1. python培训班靠谱吗-Python培训机构就业靠不靠谱?
  2. 语音识别及其定点DSP实现
  3. MongoDB创建集合及创建文档(非常详细哦~)
  4. 最简单的基于FFmpeg的libswscale的示例附件:测试图片生成工具
  5. ITU-T Technical Paper: NP, QoS 和 QoE的框架以及它们的区别
  6. bootsect.exe linux,bootsect.exe
  7. layui table reload post请求_基于Layui组件封装的后台模版
  8. python困境_学习 Python 编程的三种困境
  9. phpmyadmin/scripts/setup.php,Linux下phpMyAdmin安装过程中的问题解决
  10. 计算机上机单招试题及答案,全国高职单招计算机类模拟试卷考卷及答案