博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
volatile关键字的含义
阅读量:5777 次
发布时间:2019-06-18

本文共 2458 字,大约阅读时间需要 8 分钟。

hot3.png

public class Counter {     public static int count = 0;     public static void inc() {         //这里延迟1毫秒,使得结果明显        try {            Thread.sleep(1);        } catch (InterruptedException e) {        }         count++;    }     public static void main(String[] args) {         //同时启动1000个线程,去进行i++计算,看看实际结果         for (int i = 0; i < 1000; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    Counter.inc();                }            }).start();        }         //这里每次运行的值都有可能不同,可能为1000        System.out.println("运行结果:Counter.count=" + Counter.count);    }}

多个线程对一个静态变量进行操作的时候。多个线程可能有冲突。

运行结果:Counter.count=995

在变量之前添加volatile之后。

public class Counter {     public volatile static int count = 0;     public static void inc() {         //这里延迟1毫秒,使得结果明显        try {            Thread.sleep(1);        } catch (InterruptedException e) {        }         count++;    }     public static void main(String[] args) {         //同时启动1000个线程,去进行i++计算,看看实际结果         for (int i = 0; i < 1000; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    Counter.inc();                }            }).start();        }         //这里每次运行的值都有可能不同,可能为1000        System.out.println("运行结果:Counter.count=" + Counter.count);    }}

运行结果:Counter.count=992。

原因:

jvm运行时刻内存的分配。其中有一个内存区域是jvm虚拟机栈,每一个线程运行时都有一个线程栈,线程栈保存了线程运行时候变量值信息。当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存变量的具体值load到线程本地内存中,建立一个变量副本,之后线程就不再和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,在修改完之后的某一个时刻(线程退出之前),自动把线程变量副本的值回写到对象在堆中变量。这样在堆中的对象的值就产生变化了。下面一幅图

描述这写交互:

read and load 从主存复制变量到当前工作内存

use and assign  执行代码,改变共享变量值 
store and write 用工作内存数据刷新主存相关内容

其中use and assign 可以多次出现

但是这一些操作并不是原子性,也就是 在read load之后,如果主内存count变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化,所以计算出来的结果会和预期不一样。

(原子性:一个事务或者一段程序,当它们能完整执行或者一点都不执行的话,不会只执行一半而终止。则称这种特性叫原子性。)、

对于volatile修饰的变量,jvm虚拟机只是保证从主内存加载到线程工作内存的值是最新的。

例如假如线程1,线程2 在进行read,load 操作中,发现主内存中count的值都是5,那么都会加载这个最新的值

在线程1堆count进行修改之后,会write到主内存中,主内存中的count变量就会变为6

线程2由于已经进行read,load操作,在进行运算之后,也会更新主内存count的变量值为6

导致两个线程及时用volatile关键字修改之后,还是会存在并发的情况。

 

volatile让变量每次在使用的时候,都从主存中取。而不是从各个线程的“工作内存”。

volatile具有synchronized关键字的“可见性”,但是没有synchronized关键字的“并发正确性”,也就是说不保证线程执行的有序性。也就是说,volatile变量每次使用,线程都能得到当前volatile变量的最新值。但是volatile变量并不保证并发的正确性-------------------------每次要用到这个变量之前都要确定这个值是最新的,但是用的时候是不是最新的(其他线程是否更改)就不知道了。(和普通的有区别吗?)

 

 

 

 

 

 

 

 

转载于:https://my.oschina.net/marjeylee/blog/761813

你可能感兴趣的文章
Alibaba Dubbo框架同步调用原理分析-1
查看>>
练习与回顾
查看>>
闭包的7种形式
查看>>
进程管理supervisor的简单说明
查看>>
【软件周刊第 22 期】Chrome 58 测试版支持 PWA 沉浸式全屏体验;ECharts 新增日历坐标系和 echarts 统计扩展...
查看>>
Linux -- epoll 事件模型详解
查看>>
PostgreSQL并发控制(MVCC, 事务,事务隔离级别)
查看>>
12月19日一周一次【Python基础语法】
查看>>
DM***的第二阶段OSPF
查看>>
C# 列表 - List
查看>>
python socket编程
查看>>
20180702搭建青岛RAC记录
查看>>
安装部署TIDB分布式数据库
查看>>
Spring Security OAuth 实现OAuth 2.0 授权
查看>>
linux文件及简单命令学习
查看>>
dubbo源码分析-架构
查看>>
新 Terraform 提供商: Oracle OCI, Brightbox, RightScale
查看>>
我是新来的
查看>>
移动oa软件如何实现小屏幕大协同?
查看>>
6套毕业设计PPT模板拯救你的毕业答辩
查看>>