如果一个线程对共享变量的修改,能够被其它线程看到,那么就能说明共享变量在线程之间是可见的。如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量。Java内存模型(Java Memory Model,JMM)描述了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取出变量这样的底层细节。所有的变量都储存在主内存中。每个线程都有自己独立的工作内存,里面保存了该线程使用到的变量的副本(主内存中该变量的一份拷贝),如下图所示。

为什么会出现共享变量可见性的问题,这是因为线程对共享变量的所有操作都必须在自己的工作内存中进行,不能从主内存中读写;而且不同线程之间无法直接访问其它线程工作内存中的变量,线程间变量值的传递需要通过主内存来完成。线程1对共享变量的修改要想被线程2及时看到,必须要经过如下两个步骤:
1. 把工作内存1中更新过的共享变量刷新到主内存中;
2. 把内存中最新的共享变量的值更新到工作内存2中
Java语言层面支持的可见性实现方式有两种:
1. synchronized
2. volatile

synchronized不仅能通过互斥锁来实现同步,而且还能够实现可见性。Java内存模型关于Synchronized有两条规定:
* 线程释放锁之前,JMM会将工作内存中的共享变量刷新到主内存中;
* 线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值
线程执行互斥代码的过程:
1. 获取监视器锁
2. 清空工作内存
3. 从主内存中拷贝变量的最新副本到工作内存
4. 执行代码
5. 将更改后的共享变量的值刷新到主内存
6. 释放监视器锁
如果某个任务处于一个对标记为synchronized的方法的调用中,那么在这个线程从该方法返回之前,其它所有要调用类中任何标记为synchronized方法的线程都会被阻塞。
volatile通过加入内存屏障和禁止指令重排序优化来实现的:
* 对volatile变量执行写操作时,会在写操作后加入一条store屏障指令,这样就会把读写时的数据缓存加载到主内存中;
* 对volatile变量执行读操作时,会在读操作前加入一条load屏障指令,这样就会从主内存中加载变量;
所以说,volatile变量在每次被线程访问时,都强迫从主内存中重读该变量的值,而当该变量发生变化时,就会强迫线程将最新的值刷新到主内存,这样任何时刻,不同的线程总能看到该变量的最新值。
线程写volatile变量的过程:
1. 改变线程工作内存中volatile变量副本的值;
2. 将改变后的副本的值从工作内存刷新到主内存中
线程读volatile变量的过程:
1. 从主内存中读取volatile变量的最新值到线程的工作内存中;
2. 从工作内存中读取volatile变量的副本

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

  1. 一个Java内存可见性问题的分析

    如果熟悉Java并发编程的话,应该知道在多线程共享变量的情况下,存在“内存可见性问题”: 在一个线程中对某个变量进行赋值,然后在另外一个线程中读取该变量的值,读取到的可能仍然是以前的值: 这里并非说的 ...

  2. 从一个小例子引发的Java内存可见性的简单思考和猜想以及DCL单例模式中的volatile的核心作用

    环境 OS Win10 CPU 4核8线程 IDE IntelliJ IDEA 2019.3 JDK 1.8 -server模式 场景 最初的代码 一个线程A根据flag的值执行死循环,另一个线程B只 ...

  3. Java内存可见性volatile

    概述 JMM规范指出,每一个线程都有自己的工作内存(working memory),当变量的值发生变化时,先更新自己的工作内存,然后再拷贝到主存(main memory),这样其他线程就能读取到更新后 ...

  4. java 内存可见性

    java线程 -> 线程工作内存 -> 主物理内存 线程工作内存的原理是栈内是连续的小空间,寻址速度比堆快得多,将变量拷贝到栈内生成副本再操作 什么是重排序 代码指令可能并不是严格按照代码 ...

  5. 从原子类和Unsafe来理解Java内存模型,AtomicInteger的incrementAndGet方法源码介绍,valueOffset偏移量的理解

    众所周知,i++分为三步: 1. 读取i的值 2. 计算i+1 3. 将计算出i+1赋给i 可以使用锁来保持操作的原子性和变量可见性,用volatile保持值的可见性和操作顺序性: 从一个小例子引发的 ...

  6. 1 Java线程的内存可见性

    Java内存的可见性 可见性: 一个线程对共享变量的修改,能够及时被其它线程看到 共享变量: 如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量 Java内存模型(JM ...

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

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

  8. Java内存模型——可见性

    /** * 可见性问题 * @author Snway * */public class Visibility {        private static boolean stop;        ...

  9. Java内存模型JMM与可见性

    Java内存模型JMM与可见性 标签(空格分隔): java 1 何为JMM JMM:通俗地讲,就是描述Java中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这 ...

随机推荐

  1. canvas里设置width和css里设置width和js里设置width的区别

    canvas.width 和 它的style.width是不一样的: canvas是个画布,有他自己的宽和高(默认是没有单位的纯数字),就是canvas.width和canvas.height的宽和高 ...

  2. vue中的图标字体引入

    网址:https://icomoon.io/app/#/select: 特点:样式多,免费 操作: 1.相中的,随便点,不要钱,generat fonts然后download,得到一个压缩文件,解压, ...

  3. sql 2005性能调优

    转自:http://www.cnblogs.com/MR_ke/archive/2010/08/25/1807856.html SQL Server在运行一段时间,随着数据的积累,SQL运行效率会逐步 ...

  4. 洛谷P3376 【模板】网络最大流

    题目描述 如题,给出一个网络图,以及其源点和汇点,求出其网络最大流. 输入输出格式 输入格式: 第一行包含四个正整数N.M.S.T,分别表示点的个数.有向边的个数.源点序号.汇点序号. 接下来M行每行 ...

  5. Linux:dd命令

    dd:“data duplicator” 意为数据复印机,它可以用来拷贝和转换数据. 备份和恢复整个硬盘或分区 备份MBR(主引导记录) 在ASCII和EBCDIC格式之间转换 也可以为Linux内核 ...

  6. python->读写excel

    from openpyxl import load_workbook#将一个excel文档中的数据存放内存中,即变量wb保存了该excel的所有信息wb = load_workbook(r" ...

  7. com.mysql.jdbc.Driver 与 org.gjt.mm.mysql.Driver的区别

    com.mysql.jdbc.Driver的前身是org.gjt.mm.mysql.Driver,现在主要用com.mysql.jdbc.Driver,但为了保持兼容性保留了org.gjt.mm.my ...

  8. 关于Oracle归档的一些操作

    1.查看日志模式archive log list,或者select name,log_mode from v$database; 2.数据库非归档模式改为归档模式: 关闭数据库:shutdown im ...

  9. .net core使用ef core操作mysql数据库

    新建.net core webapi项目 在NuGet包管理器中搜索 MySql.Data.EntityFrameworkCore并安装,安装的8.0.14版本,只安装这一个就够了 安装后创建Data ...

  10. 第六篇——Struts2的后缀

    Struts2后缀 1.Struts2默认后缀是action: 2.Struts2使用默认后缀时 *.aciton 和 * 都是同一个请求: 3.Struts2自定义后缀后只能使用自定义的后缀访问: ...