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关键字 保证可见性和防止指令重排 不能保证原子性 计算机系统的一致性 在现代计算 ...
随机推荐
- spring aop无法拦截类内部的方法调用
1.概念 拦截器的实现原理就是动态代理,实现AOP机制.Spring 的代理实现有两种:一是基于 JDK Dynamic Proxy 技术而实现的:二是基于 CGLIB 技术而实现的.如果目标对象实现 ...
- Visual Studio安装SVN插件
VS的SVN插件 材料 VS安装程序. VisualSVN安装程序,点击下载. VisualSVN-5.0.1 前期准备 在代码管理的服务器上安装SVN server,可参考svn安装部署以及服务器 ...
- H2数据库
官网:http://www.h2database.com H2数据库默认的~/test数据库在Win10下所在的位置为 C:/Users/yourname/下,也就是执行cmd的第一个目录 其中的.h ...
- npm cnpm
npm 1.说明: npm(node package manager)是nodejs的包管理器,用于node插件管理(包括安装.卸载.管理依赖等) 2.使用npm安装插件:命令提示符执行npm ins ...
- msvcp140.dll丢失解决方案
[首先给出99%情况下都能解决这个问题的最简单办法]: 下载并安装微软VC++2015版运行库 (英文全称:Microsoft Visual C++ 2015 Redistributable Pa ...
- SpringSecurity-UsernamePasswordAuthenticationFilter的作用
UsernamePasswordAuthenticationFilter应该是我们最关注的Filter,因为它实现了我们最常用的基于用户名和密码的认证逻辑. 先看一下一个常用的form-login配置 ...
- Java中用字符串常量赋值和使用new构造String对象的区别
String str1 = "ABC"; String str2 = new String("ABC"); String str1 = “ABC”;可能创建一个 ...
- Dapper/SqlMapper映射对应问题
Dapper, a light weight object mapper for ADO.NET 轻量级orm辅助类,不用配置,直接引入一个cs文件或者引入其dll就能使用. 使用连接对象的query ...
- 原生js中用Ajax进行get传参
原生js中用Ajax进行get传参 案例: <!DOCTYPE html> <html> <head> <meta charset="UTF-8&q ...
- 冷知识点:COLLATE 关键字是什么意思?
mysql 数据库表: CREATE TABLE `book_order_test` ( `order_id` varchar(50) COLLATE utf8_bin DEFAULT NULL CO ...