文档章节

volatile关键字解析

HoiCai
 HoiCai
发布于 2018/11/23 15:57
字数 1247
阅读 0
收藏 0

一、内存模型的相关概念

计算机在执行程序时,指令的执行都是在CPU中。在执行指令的过程中,会将临时变量存储在主存(物理内存)中。 CPU执行指令很快,而从内存中读取和写入数据却相对来说很慢,所以就有了高速缓存。 在程序运行时,先将数据从主存复制一份到高速缓存中,然后CPU执行指令时,就可以直接从高速缓存中读取和写入数据,然后刷新到内存中。

这里就产生了一个问题。 比如i是一个共享变量,初始值为0

i = i+1;

在单线程中,CPU执行指令时,先从高速缓存中取出数据,然后进行加1操作,再写入高速缓存中,然后刷新到主存中。 但是多线程中,每一个线程都有自己的工作缓存,线程A,B从高速缓存中读取到数据i的值都为0,在进行加1操作后,写入自己的工作缓存中,再刷新到内存中。 进行两次加法操作,值却为1。

这就是缓存不一致问题。

解决缓存不一致有两个解决方法。

1.在总线加入LOCK#锁

2.使用缓存一致性协议

总线加入锁来阻塞其他线程访问时,会导致线程阻塞,使效率低下。

缓存一致性协议的核心思想是:当线程对一个共享变量进行写操作时,会发出通知,让其他的线程该变量的缓存行置为无效,其他线程操作该变量时,会重新从内存中读取数据。

二、JAVA内存模型

Java内存模型规定所有的变量都存在主存中,每个线程都有自己的工作内存(高速缓存),线程的所有操作都只能操作工作内存,不能操作主存,也不能操作其他线程的工作内存。

三、并发编程中的三个概念

1.原子性

一个操作或多个操作,全部执行不会被其他操作打断。 原子性只针对读取和写入指令。 比如

i = i + 1;

这个代码是没有原子性的,先从高速缓存中取出i的数据,然后进行加1操作,最后写入高速缓存,再刷新到内存中。

如果要保证这个操作的原子性,就可以通过synchronized和lock来实现。

2.可见性

多个线程访问同一个变量时,一个线程修改了变量的值,其他线程能立即看到修改的值。

Java提供了volatile来保证有效性。

当一个共享变量被volatile修饰时,它保证当线程修改它的值时立即被更新到主存中,其他线程读取时,从主存中读取值。

3.有序性

程序执行顺序按照代码的先后顺序执行。

处理器为了提高程序运行效率,可能会对代码执行顺序进行优化。这里可能会发生指令重排序。

比如两个赋值语句

context = init()
boolean flag = true;

处理器在执行时,可能先执行flag = true,再执行初始化。

多线程执行时,如果后面有根据flag为真,对初始化内容的操作。线程1先执行flag=true,还未初始化内容,线程2根据flag为真执行了对内容的操作,就会导致异常。

while(!flag){
    sleep()
}
dosomething(context);

可以用synchronized和look保证每个时刻只有一个线程访问代码块,相当于顺序执行代码。

四、深入剖析volatile关键字

  1. 保证不同线程对这个变量具有可见性,即一个线程修改了某个变量的值,这个新值对其他线程是立即可见的。
  2. 被volatile修饰的变量禁止指令重排序

多个线程对一个volatile修饰的值进行操作时,可能会导致原子性不一致的问题。 因为volatile只保证了可见性和有序性。

对于自增代码

i++;

由于该操作不是原子性,所以可能存在一个线程从内存中取出值,之后另一个线程对该数据取值操作写入内存。因为可见性是指读操作,之前线程的值已经取出来了,不会变,之后操作得到的值就是1,而不是2。

解决方法是该自增代码加Lock或synchronized

© 著作权归作者所有

HoiCai
粉丝 4
博文 30
码字总数 19923
作品 0
深圳
程序员
私信 提问
java并发编程:关键字

一、关键字volatile(易变的): 1、保证了多线程操作的可见性; 2、但是无法保证对变量的任何操作都是原子性的(如自增操作); 3、禁止指令重排序,能在一定程度上保证有序性; 禁止指令重...

rathan0
2015/09/16
49
0
Java 单例真的写对了么?

单例模式是最简单的设计模式,实现也非常“简单”。一直以为我写没有问题,直到被 Coverity 打脸。 1. 暴露问题 前段时间,有段代码被 Coverity 警告了,简化一下代码如下,为了方便后面分析...

zjg23
2016/05/08
22
0
Java并发编程之volatile关键字解析

原文出处海子 一内存模型的相关概念 二并发编程中的三个概念 三Java内存模型 四深入剖析volatile关键字 五使用volatile关键字的场景 volatile这个关键字可能很多朋友都听说过,或许也都用过。...

大黄有故事
2017/02/09
0
0
Java并发学习之Volatile及内存模型探究

volatile工作原理 java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量。 Java语言提供了volatile,在某些情况下比锁更加方便...

小灰灰Blog
2017/11/02
52
0
2017十年腾讯大牛讲解---- Java并发编程:volatile关键字解析

我做为一个工作10年的JAVA从业人员分享一下我的一些经验,现在解析一下Volatile关键词 volatile这个关键字可能很多朋友都听说过,或许也都用过。在Java 5之前,它是一个备受争议的关键字,因...

JAVA大神
2017/11/22
0
0

没有更多内容

加载失败,请刷新页面

加载更多

springboot初探---spring-boot-starter-web究竟干了啥

上一篇已经简单介绍了启动类的部分,这一篇主要讨论一下springboot引入的哪些依赖 我们都知道想用springboot做一个web应用,首先要做的是引入相关依赖,两步操作: 1、添加spring-boot-start...

计算机狼
30分钟前
5
0
基于Rocket.chat搭建内网聊天系统(使用docker,本机不需要安装meteor)

您可能不希望使用标准的Docker命令,而是希望对部署进行更多的自动化管理。这就是使用Docker-compose可能会派上用场的地方。 确保您已安装Docker和Docker-compose并且可以正常运行。 docker...

吴伟祥
32分钟前
6
0
conda 更新源

更新conda 源为阿里源 conda config --add channels http://mirrors.aliyun.com/pypi/simple conda config --set show_channel_urls yes 阿里云: http://mirrors.aliyun.com/pypi/simple/ 豆......

Mr_Tea伯奕
32分钟前
4
0
java 泛型使用

每次写泛型方法都翻下百度,还是自己记录下把。 1、定义一个泛型方法,使用传入参数类型来传递泛型。这种用法在封装json序列化工具类应该会用到。 List<xxx> aa = getList(xxx.class);pr...

朝如青丝暮成雪
36分钟前
6
0
深入了解Java模板引擎Freemarker

前言 常用的Java模板引擎包括:JSP、Freemarker、Thymeleaf、Velocity,从Github上查阅到这几款主流的模板引擎的性能的对比,总体上看,JSP、Freemarker、Thymeleaf、Velocity在性能上差别不...

code-ortaerc
38分钟前
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部