Java内存的可见性

可见性: 一个线程对共享变量的修改,能够及时被其它线程看到

共享变量: 如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量

Java内存模型(JMM): 描述了Java程序中各种线程共享变量的访问规则,以及在JVM中将线程共享变量存储到内存和从内存中读取出线程共享变量这样的底层细节

上面这些规则都是针对线程的共享变量的,JMM的细节会在以后的博客里面写。 本篇只需要知道

  • 1 所有的变量都存储在主内存中
  • 2 每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本,主要是针对共享变量

下面使用一张图来表示主内存,工作内存(也叫做本地内存),线程,共享变量之间的关系

由上图可知:

1 每个线程都有自己的工作线程

2 每个线程只能操作自己的工作内存,不能直接操作主内存

3 每个线程都有一个主内存中的变量a的一个副本,所以,变量a就叫做这三个线程的共享变量

在讲变量在内存中的可见性之前,先看下JMM(JAVA内存模型)中的两条规定

1 线程对共享变量的所有的操作都必须在自己的工作内存中进行,不能直接从主内存中读写

2 不同线程之间无法直接访问其它线程工作内存中的变量,线程间变量的传递需要通过主内存来完成

这两条规定也可以从上面的图中可以看出来。

共享变量可见性实现的原理

问:线程1对共享变量的修改如何被线程2及时的看到? 主要经过以下2个步骤

  • 把线程1中的工作线程中,更新过的共享变量a,刷新到主内存中
  • 将主内存中最新的共享变量a的值,更新到线程2的工作内存中

经过了上面两个步骤后,线程1对共享变量的修改,及时的更新到主内存中

线程2将主内存中的最新的共享变量的值,刷新到自己的工作内存中

线程1 和 线程2 中,a的值就一样了,都是最新的,这时候就说共享变量a在线程1和线程2中是可见的。

由于上面的JMM的两条规定,线程都是在自己的工作线程中操作变量,线程不能直接和主内存进行交互,线程之间必须通过主内存进行交互

在多线程编程中,就会出现下面这两种情况:

  • 线程1 修改了共享变量的值,但是没有及时的更新到主内存中去,线程2读取到的值就不是最新的。
  • 线程2 修改并及时的更新了共享变量的值,但是线程2没有及时的读取到,也会导致线程读到到的值不是最新的。

由此产生了共享变量a在线程1和线程2中是不可见的,理想的情况下,我们想要实现的是共享变量对所有访问它的线程都是可见的。

不可见往往会导致很多严重的问题,导致数据的不一致性。多线程编程中,要保证线程间的可见性

如何实现共享变量的可见性?要实现共享变量的可见性,必须保证两点:

  • 线程修改后的共享变量的值能够及时从工作内存中刷新到主内存中
  • 其它线程能够及时把共享变量的最新值从主内存更新到自己的工作内存中

JAVA在语言层面支持的可见性实现方式有哪些?

  • synchronized
  • volatile

1  synchronized实现可见性。

synchronized 的两个作用:

1 原子性(同步)

2 可见性

很多同学对第一种synchronized同步比较了解,都知道。也经常用,其实synchronized还能实现内存的可见性的。

 JMM关于synchronized的两条规定:

  • 线程解锁前,必须把共享变量的最新值刷新到主内存中
  • 线程加锁时,将清空工作内存中共享变量的值,然后从主内存中重新读取共享变量的值(注:加锁与解锁需要是同一把锁)

以上两条规定保证了  线程解锁前对共享变量的修改在下次加锁时对其它线程可见

synchronized线程执行互斥锁代码的过程如下:

1 在synchronized的入口处,获得互斥锁

2 获得互斥锁后,清空工作内存

3 从主内存中拷贝变量的最新值到工作内存

4 执行代码

5 执行完代码后,共享变量的值有可能发生变化,这时会将共享变量的值刷新到主内存中

6 释放锁

在演示代码前,先了解一个事件

问:程序的执行顺序一定是按照代码的书写的顺序执行的吗?

答:答案是 否是的。即不一定是按照代码的书写顺序执行的,主要是因为编译器或者处理器做了优化,即指令重排序

问:为什么要有重排序?重排序有什么好处?

答:编译器或者处理器为了提高程序的性能而做的优化,更加符合处理器执行效率。

问:指令重排序不会打乱了程序的逻辑吗?

答:不会,因为JMM有一条规定,as-if-serial原则,即保证在单线程里面,指令重排序前和重排序后,执行的结果是一致的。

如下面的例子,a,b的赋值顺序不同,但不会影响sum的值,注:是在单线程里面。

重排序前:                 重排序后:

a = 1                       b = 1

b = 1                       a = 1

sum = a + b              sum = a + b

指令重排序:代码书写的顺序与实际执行的顺序不同,指令重排序是编译器或者处理器为了提高程序性能而做的优化

  • 编译器优化的重排序(编译器优化)
  • 指令级并行重排序(处理器优化)
  • 内存系统的重排序(处理器优化)

as-if-serial原则:单线程里,无论怎么重排序,程序的执行结果是一致的

如上面的例子,无论前面两句怎么排序,最后一句sum = a + b 是不能排序的。这样就保证了程序的结果一致性

结论:

1 重排序不会给单线程带来内存可见性问题

2 多线程程序交错执行时,重排序可能会造成内存可见性问题

今天就先写到这里,下一篇会有代码来讲演示上面的理论

1 Java线程的内存可见性的更多相关文章

  1. 细说Java多线程之内存可见性

    编程这些实践的知识技能,每一次学习使用可能都会有新的认识 一.细说Java多线程之内存可见性(数据挣用)         1.共享变量在线程间的可见性                共享变量:如果一个 ...

  2. Java多线程之内存可见性

    阅读本文约“3分钟” 共享变量在线程间的可见性 synchronized实现可见性 volatile实现可见性 —指令重排序 —as-if-serial语义 —volatile使用注意事项 synch ...

  3. Java多线程之内存可见性和原子性:Synchronized和Volatile的比较

    Java多线程之内存可见性和原子性:Synchronized和Volatile的比较     [尊重原创,转载请注明出处]http://blog.csdn.net/guyuealian/article ...

  4. Java线程工作内存与主内存变量交换过程及volatile关键字理解

    Java线程工作内存与主内存变量交换过程及volatile关键字理解 1. Java内存模型规定在多线程情况下,线程操作主内存变量,需要通过线程独有的工作内存拷贝主内存变量副本来进行.此处的所谓内存模 ...

  5. 【Java线程与内存分析工具】VisualVM与MAT简明教程

    目录 前言 VisualVM 安装与配置 本地使用 远程监控 MAT 使用场景 安装与配置 获得堆转储文件 分析堆转储文件 窥探对象内存值 堆转储文件对比分析 总结 前言 本文将简要介绍Java线程与 ...

  6. java 线程​基本概念 可见性 同步

    开发高性能并发应用不是一件容易的事情.这类应用的例子包括高性能Web服务器.游戏服务器和搜索引擎爬虫等.这样的应用可能需要同时处理成千上万个请求.对于这样的应用,一般采用多线程或事件驱动的架构.对于J ...

  7. java多线程之内存可见性-synchronized、volatile

    1.JMM:Java Memory Model(Java内存模型) 关于synchronized的两条规定: 1.线程解锁前,必须把共享变量的最新值刷新到主内存中 2.线程加锁时,将清空工作内存中共享 ...

  8. 细说Java多线程之内存可见性笔记

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 说明:多线程的内存可见性涉及到多线程间的数据争用,也涉及到了多线程间的数据可见性 一.共享变量在线程间的 ...

  9. java线程--volatile实现可见性

    volatile关键字: 1)能够保证volatile变量的可见性 2)不能保证volatile变量复杂操作的原子性. volatile如何实现内存可见性: 深入来说:通过加入内存屏障和禁止重排序优化 ...

随机推荐

  1. Spark的数据存储

    Spark本身是基于内存计算的架构,数据的存储也主要分为内存和磁盘两个路径.Spark本身则根据存储位置.是否可序列化和副本数目这几个要素将数据存储分为多种存储级别.此外还可选择使用Tachyon来管 ...

  2. js关于页面坐标api

    网页可见区域宽: document.body.clientWidth;网页可见区域高: document.body.clientHeight;网页可见区域宽: document.body.offset ...

  3. highCharts 饼图动态加载

    饼图的动态加载 (1):导入样式 <script type="text/javascript" src="<%=request.getContextPath( ...

  4. faster-rcnn(testing): ubuntu14.04+caffe+cuda7.5+cudnn5.1.3+opencv3.0+matlabR2014a环境搭建记录

    python版本的faster-rcnn见我的另一篇博客: py-faster-rcnn(running the demo): ubuntu14.04+caffe+cuda7.5+cudnn5.1.3 ...

  5. 设置TextBox控件的TextMode属性

    我想在程式代碼中將TextBox控件的TextMode属性设置為Password,寫成TextBox1.TextMode=MultiLine和TextBox1.TextMode="Multi ...

  6. JQuery实现的模块交换动画效果

    <!doctype html> <html> <head> <meta http-equiv="content-type" content ...

  7. PHP判断请求是否是ajax请求

    首先看一下框架里面是怎样判断的.ThinkPHP:define('IS_AJAX', ((isset($_SERVER['HTTP_X_REQUESTED_WITH']) && str ...

  8. apche 虚拟主机设置

    <要求>:两个不同的域名 www.got7.com 和www.wgayi.com 指向同一个IP地址当在浏览器中输入不同的域名时.对应不同的网站根目录 备注:域名got7,wgayi,纯属 ...

  9. python实现监控URL的一个值小于规定的值--邮件报警

    监控URL的一个值小于规定的值--邮件报警 #!/usr/bin/env python #-*- coding:utf-8 -*- __author__ = 'liudong' import urll ...

  10. Appium学习路—Android定位元素与操作

    一.常用识别元素的工具 uiautomator:Android SDK自带的一个工具,在tools目录下 monitor:Android SDK自带的一个工具,在tools目录下 Appium Ins ...