Volatile 关键字 内存可见性
1、问题引入
实现线程:
public class ThreadDemo implements Runnable
{
private boolean flag = false; @Override
public void run()
{
flag = true;
System.out.println("flag=" + flag);
} public boolean isFlag()
{
return flag;
} public void setFlag(boolean flag)
{
this.flag = flag;
}
}
测试类:
public class VolatileTest
{
public static void main(String[] args)
{
ThreadDemo threadDemo = new ThreadDemo();
new Thread(threadDemo).start(); while (true)
{
if(threadDemo.isFlag())
{
System.out.println("----------------");
break;
}
}
}
}

结果:flag=true,并且程序不会停止
结果分析:从结果中看到,线程threadDemo 将 flag 被修改为 ture 了,但是 while 循环中的 if 判断中没有进入(即 flag = false);主线程中的flag和threadDemo 中的flag值不一样。

内存可见性的问题:多个线程操作共享数据时,各个线程之间的数据不一致;
JVM会为每个线程分配一个独立的缓存用于提高效率。
(1)程序开始运行时主存中的flag=false;
(2)接着线程threadDemo会修改flag数据:threadDemo先从主存中获取到flag数据(false),然后在线程threadDemo中的缓存数据修改为true;
(3)在这个时候main线程来了,main线程从主存中读取到的flag=false;
(4)线程threadDemo将flag=true写入到主存中;
(5)while(true) 的执行效率特别高,以至于 main 线程没有机会从主存中读取数据;
解决内存可见性的问题:
1、增加同步锁,用 synchronized 进行同步,代码如下:
public class VolatileTest
{
public static void main(String[] args)
{
ThreadDemo threadDemo = new ThreadDemo();
new Thread(threadDemo).start(); while (true)
{
synchronized (threadDemo)
{
if (threadDemo.isFlag())
{
System.out.println("----------------");
break;
}
}
}
}
}
以上代码存在的问题:增加同步锁解决了内存可见性的问题,但是效率特别低;
2、增加 volatile 关键字,修饰线程的共享数据,代码如下:
public class ThreadDemo implements Runnable
{
private volatile boolean flag = false; @Override
public void run()
{
flag = true;
System.out.println("flag=" + flag);
} public boolean isFlag()
{
return flag;
} public void setFlag(boolean flag)
{
this.flag = flag;
}
}

Volatile关键字不具备“互斥性”,Volatile不能保证“原子性”;
Java内存模型(Java Memory Model)描述了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取出变量这样的底层细节。
所有的变量都存储在主内存中。每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝),如图

两条规定:
- 线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读取
- 不同线程之间无法直接访问其他线程工作内存中的变量,线程间变量值的传递需要通过主内存来完成。
在这种模型下会存在一个现象,即缓存中的数据与主内存的数据并不是实时同步的,各CPU(或CPU核心)间缓存的数据也不是实时同步的。这导致在同一个时间点,各CPU所看到同一内存地址的数据的值可能是不一致。
volatile关键字:
能够保证volatile变量的可见性,不能保证volatile变量复合操作的原子
volatile如何实现内存的可见性:
深入来说:通过加入内存屏障和禁止重排序优化来实现的
在每个volatile写操作前插入StoreStore屏障,在写操作后插入StoreLoad屏障;
在每个volatile读操作前插入LoadLoad屏障,在读操作后插入LoadStore屏障;
通俗地讲:volatile变量在每次被线程访问时,都强迫从主内存中重读该变量的值,而当该变量发生变化时,又会强迫将最新的值刷新到主内存。这样任何时刻,不同的线程总能看到该变量的最新值。
线程写volatile变量的过程:
(1)改变线程工作内存中volatile变量副本的值
(2)将改变后的副本的值从工作内存刷新到主内存
线程读volatile变量的过程:
(2)从主内存中读取volatile变量的最新值到线程的工作内存中
(2)从工作内存中读取volatile变量的副本
参考链接:
https://www.cnblogs.com/amei0/p/8378625.html
Volatile 关键字 内存可见性的更多相关文章
- 二、volatile关键字 - 内存可见性
1.内存可见性 (程序在运行时,jvm会为每一个执行任务的线程都分配一个独立的缓存,用于提高效率) 我觉得可以这样来理解: 内存:啥是内存?就是可以理解成电脑当中的内存条,程序创建个变量, ...
- java多线程 -- volatile 关键字 内存 可见性
内存可见性(Memory Visibility) 1 内存可见性(Memory Visibility)是指当某个线程正在使用对象状态而另一个线程在同时修改该状态,需要确保当一个线程修改了对象状态后,其 ...
- 1.volatile关键字 内存可见性
Java JUC 简介 在 Java 5.0 提供了 java.util.concurrent (简称JUC )包,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括 ...
- 1. volatale 关键字 -内存可见性
package com.gf.demo01; /** * 一.volatile 关键字:但多个线程进行操作共享数据时,可以保证内存中数据可见性. * */ public class TestVolat ...
- 1、JUC--volatile 关键字-内存可见性
Java JUC简介 在 Java 5.0 提供了 java.util.concurrent (简称JUC )包,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括线 ...
- JUC 并发编程--05, Volatile关键字特性: 可见性, 不保证原子性,禁止指令重排, 代码证明过程. CAS了解么 , ABA怎么解决, 手写自旋锁和死锁
问: 了解volatile关键字么? 答: 他是java 的关键字, 保证可见性, 不保证原子性, 禁止指令重排 问: 你说的这三个特性, 能写代码证明么? 答: .... 问: 听说过 CAS么 他 ...
- 全面理解Java内存模型(JMM)及volatile关键字(转载)
关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoad ...
- 全面理解Java内存模型(JMM)及volatile关键字
[版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/72772461 出自[zejian ...
- Java并发编程:JMM (Java内存模型) 以及与volatile关键字详解
目录 计算机系统的一致性 Java内存模型 内存模型的3个重要特征 原子性 可见性 有序性 指令重排序 volatile关键字 保证可见性和防止指令重排 不能保证原子性 计算机系统的一致性 在现代计算 ...
随机推荐
- Maya中输出alembic文件的方法
Maya中输出alembic文件是有现成api调用的,与maya中大部分api一样,这个功能参数的传入是非常类似mel的,本质上讲都是kwargs类型的参数,所以我们传入的参数就需要整理成类似于mel ...
- C#中Button.DialogResult属性
窗体中的某个按钮,如果设置了DialogResult(不是设置为None),当窗体是通过ShowDialog方法显示的时候 则不必设置任何响应函数,单击按钮也可窗体.然后,该窗体的DialogResu ...
- python永久添加环境变量
import sys sys.path 系统环境是一个list,可以将自己需要的库添加进入,例如mysql库,hive库等等.有三种方式添加,均验证通过: 1 临时添加,在一个shell窗口中 ...
- ftok()函数深度解析
[转载] 原文链接:https://blog.csdn.net/u013485792/article/details/50764224 关于ftok函数,先不去了解它的作用来先说说为什么要用它,共享内 ...
- 涨姿势:Java 分业务、分级别实现自定义日志打印
自定义日志级别 通常的日志框架都有以下几个级别,从低到高TRACE,DEBUG,INFO,WARN,ERROR,FATAL. 默认情况,假如我们定义日志打印级别INFO,它会把大于等于INFO级别的日 ...
- spring mvc 注解整理(一)
@Controller和@RestController: RestController = @ResponseBody + @Controller 所有返回都是json类型,无法跳转到jsp页面,但 ...
- 刘志梅2017710101152.《面向对象程序设计(java)》第一周学习总结
本人学号<面向对象程序设计(java)>第一周学习总结 第一部分:课程准备部分 填写课程学习 平台注册账号, 平台名称 注册账号 博客园:www.cnblogs.com http://ww ...
- matplotlib初识
Matplotlib 能够创建多数类型的图表,如条形图,散点图,条形图,饼图,堆叠图,3D 图和地图图表. import matplotlib.pyplot as plt plt.plot([,,], ...
- 版本控制(svn、Git)环境创建
1.SVN 2.Git last 版本控制工具应该具备的功能: 协同修改: 数据备份: 版本管理: 权限控制: 历史记录:查看修改人.修改时间.修改内容…… 分支管理:开发团队在工作过程中多条产品线同 ...
- SecureCR 改变背景色和文字颜色
1.打开SecureCR链接Linux服务器,Options->Session Options->Emulation->Terminal 选择Linux (相应的服务器系统)ANSI ...