如果一个线程对共享变量的修改,能够被其它线程看到,那么就能说明共享变量在线程之间是可见的。如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量。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. lower_bound && upper_bound

     用lower_bound进行二分查找 ●在从小到大排好序的基本类型数组上进行二分查找. 这是二分查找的一种版本,试图在已排序的[first,last)中寻找元素value.如果[first,last ...

  2. 洛谷P3387 【模板】缩点

    题目背景 缩点+DP 题目描述 给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大.你只需要求出这个权值和. 允许多次经过一条边或者一个点,但是,重复经过的点,权值只 ...

  3. Ubuntu 18.04安装JDK并配置环境变量

    1.官网下载jdk 下载链接 http://www.oracle.com/technetwork/java/javase/downloads/index.html 可以根据自己的系统进行下载 2.进行 ...

  4. ios证书生成

    iOS有两种证书和描述文件: 证书类型 使用场景 开发(Development)证书和描述文件 用于开发测试 发布(Distribution)证书和描述文件 用于提交Appstore 准备环境 必需要 ...

  5. 记录常用的adb命令

    1.启动adb服务 adb start-server 2.关闭服务 adb kill-server 3.进入shell环境 adb shell 4.安装应用 adb install -r xxx.ap ...

  6. el表达式(一)

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  7. SpringBoot2.0整合mybatis、shiro、redis实现基于数据库权限管理系统

    转自https://blog.csdn.net/poorcoder_/article/details/71374002 本文主要介绍使用SpringBoot与shiro实现基于数据库的细粒度动态权限管 ...

  8. 详解C# 网络编程系列:实现类似QQ的即时通信程序

    https://www.jb51.net/article/101289.htm 引言: 前面专题中介绍了UDP.TCP和P2P编程,并且通过一些小的示例来让大家更好的理解它们的工作原理以及怎样.Net ...

  9. Qt 拷贝内容到粘贴板 || 获取粘贴板内容

    QString source = ui->textEdit_code->toPlainText(); QClipboard *clipboard = QApplication::clipb ...

  10. 020-Json结构数据序列化异步传递

    C#中将.Net对象序列化为Json字符串的方法: JavaScriptSerializer().Serialize(p),JavaScriptSerializer在System.Web.Extens ...