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关键字 保证可见性和防止指令重排 不能保证原子性 计算机系统的一致性 在现代计算 ...
随机推荐
- MYSQL存储过程实现用户登录
MYSQL存储过程实现用户登录 CREATE DEFINER=`root`@`%` PROCEDURE `uc_session_login`( ), ) ) LANGUAGE SQL NOT DETE ...
- java多线程中并发集合和同步集合有哪些?区别是什么?
java多线程中并发集合和同步集合有哪些? hashmap 是非同步的,故在多线程中是线程不安全的,不过也可以使用 同步类来进行包装: 包装类Collections.synchronizedMap() ...
- java多线程中最佳的实践方案是什么?
java多线程中最佳的实践方案是什么? 给你的线程起个有意义的名字.这样可以方便找bug或追踪.OrderProcessor, QuoteProcessor or TradeProcessor 这种名 ...
- 第二章 FFmpeg常用命令
2.1 FFmpeg常见的命令大概分为6个部分 ffmpeg信息查询部分 公共操作参数部分 文件主要操作参数部分 视频操作参数部分 字幕操作参数部分 2.1.1 FFmpeg的封装转换 FFmpeg ...
- python中定时任务
今天看网络框架时,突然想看一下定时器,于是往上搜索了一下python中timer task的实现,但是由于python本身对线程的支持不是太好,因为全局排它锁的存在,使得多线程在访问资源时效率比较低. ...
- C语言的基础输入输出
首先来整理一下各个数据类型的输入输出格式: 1.char %c 2.int/short int %d 3.long int %ld 4.long long int %lld 5.float %f 6. ...
- 学习excel的使用技巧二批量复制
1 选中要操作的部分 2 CTRL+G 打开定位 3 点击 定位条件 4 选择空值 5 输入=号 然后键盘的 方向键 向上 6 按住CTRL+回车 即可实现 批量复制
- spring 之 类型转换 2
spring内置的转换器 在spring xml 文件中,配置属性的时候, 不管实际是 list 还是map ,还是Date, 或者原生的java 类型, 我们只能配置xml 给它们. 那么 spri ...
- 清除eclipse,STS workspace历史记录
记一下 打开eclipse下的/configuration/.settings目录 修改文件org.eclipse.ui.ide.prefs文件 把RECENT_WORKSPACES这项修改为你需要的 ...
- Python第1天
今天主要学习内容如下: 概论,各种开发语言的对比,高级语言包括:python(开发效率高,执行效率低) Java(开发效率低,执行效率高),PHP,低级语言包括:C语言,汇编语言: Python 语言 ...