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. thinkPHP-空操作

    空操作 当访问的方法不存在时,可以定义一个empty方法来避免空操作 function _empty() { echo "网页不存在,请检查地址信息"; } 这样当访问不存在的方法 ...

  2. Apache 配置 WebSocket 协议

    本文使用 http proxy 方式 实现 apache 支持  WebSocket 请求(JK 使用的 ajp 协议不能支持websocket) 通过 apache 访问 后端 tomcat上的 w ...

  3. hql between and 查询

    public IList<PrdtStdEntity> QueryPrdtStd(PrdtStdEntity prdtStdEntity) { try { var hql = " ...

  4. jacob操作word

    http://wang-ping001.iteye.com/blog/1452057 ————————————————————————————————————————————————————————— ...

  5. Nginx使用Expires增加浏览器缓存加速

    Max-age是指我们的web中的文件被用户访问(请求)后的存活时间,是个相对的值,相对Request_time(请求时间). Expires它比max-age要麻烦点,Expires指定的时间分&q ...

  6. 9.7 js进阶总结2

    数组元素添加 将一个或多个新元素添加到数组结尾,并返回数组新长度 var week_len = week.push(‘星期四’,‘星期五’); 将一个或多个新元素添加到数组开始,数组中的元素自动后移, ...

  7. winform版弹框操作

    公共弹框帮助类操作手册 1.说明 封装了对于winform操作的一些提示框,包括数据加载耗时的时候,提示数据正在加载,请稍后的提示窗体,动态提示给用户一些有用的信息.例如网吧里面续费提醒等. 2.操作 ...

  8. [delphi]SetWindowsHookExA函数入口处修改

    library Project2; uses SysUtils, Classes, windows, Dialogs; {$R *.res} function GetModuleHandleA(a: ...

  9. WEB测试方法及注意地方

    1页面部分(1) 页面清单是否完整(是否已经将所需要的页面全部都列出来了)(2) 页面是否显示(在不同分辨率下页面是否存在,在不同浏览器版本中页面是是否显示)(3) 页面在窗口中的显示是否正确.美观( ...

  10. windows下手动安装和配置xamarin

    安装xamarin xamarin官方给出了两种安装方式,自动安装和手动安装. 自动安装比较简单,到http://xamarin.com/download下载xamarininstaller.exe ...