volatile关键字解析

volatile关键字解析

一、内存模型的相关概念

计算机在执行程序时,指令的执行都是在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

转载于:https://my.oschina.net/u/3371784/blog/2934209

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/101300.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 魔兽世界正式服模拟(战地模拟器破解版)

    背景:从06年玩魔兽到现在也13年了。5.48的时候在国外读研、时间特别多,在艾苏恩的“魔兽夜店”lm公会(永远记得这段快乐的时光),围攻奥格达到了我的顶峰(带团),回国后找工作6.X没玩。从7.x就开始咸鱼,H都没通。现在也得结婚成家了。以后更加咸鱼了,指不定哪天就AFK。差不多06年的时候就混迹于大芒果、藏宝湾这两个网站,虽然是高中生,那时候用家里的破电脑就开始搭建单机版,改数据库。晚上在自己…

  • INT0中断_中断请求寄存器

    INT0中断_中断请求寄存器 当进程执行系统调用时,先调用系统调用库中定义某个函数,该函数通常被展开成前面提到的_syscallN的形式通过INT0x80来陷入核心,其参数也将被通过寄存器传往核心。 在这一部分,我们将介绍INT0x80的处理函数system_call。 思考一下就会发现,在调用前和调用后执行态完全不相同:前者是在用户栈上执行用户态程序,后者在核心栈上执行核心态代码。那么,为了保证在核心内部执行完系统调用…

  • Google资深工程师深度讲解Go语言-测试与性能调优(八)

    Google资深工程师深度讲解Go语言-测试与性能调优(八)

  • 循环队列的顺序存储结构Java

    循环队列的顺序存储结构Java循环队列的顺序存储结构在上次,我们讲到的是,队列的顺序存储结构也是由ArrayList实现的,从此就可以看出,在入队时候的时间复杂度为O(1),但是在出队时候的时间复杂度为O(n),这是因为,每次在出队后要将数组后面的有效元素前移一位。所以,这里就会用到循环队列,显然,这种队列也是顺序存储结构,在这个循环队列中也会去实现接口Queue。首先,我们要想到的是如何将一般的队列改变为循环队列。…

  • Android面试题(四大组件篇)[通俗易懂]

    Android面试题(四大组件篇)[通俗易懂]Android面试题(四大组件篇)window、进程、线程篇Android面试题(数据存储、view篇)ActivityQ:说下Activity的生命周期?Q:onStart()和onResume()/onPause()和onStop()的区别?是否位于前台,对用户是否可见的区别Q:ActivityA启动另一个ActivityB会回调哪些方法?如果A…

  • 百度快照更新是什么意思啊_百度快照和百度推广的区别

    百度快照更新是什么意思啊_百度快照和百度推广的区别百度快照更新是什么意思?    最近发现有很多刚入SEO行业的新手对网站seo的技巧有很多的误区,比如网站快照不更新就代表网站被惩罚。关于这个观点我们先看看什么是百度快照?百度快照的作用是什么?我们有该如何让百度快照持续更新呢?        一、百度快照是什么?    快照即为WebCache,可以翻译为网页缓存,当搜索引擎派出蜘蛛去对网站进行索

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号