我要投搞

标签云

收藏小站

爱尚经典语录、名言、句子、散文、日志、唯美图片

当前位置:双彩网 > 执行开销 >

volatile原理 使用条件

归档日期:07-02       文本归类:执行开销      文章编辑:爱尚语录

  有时仅仅为了读写一个或者两个实例域就使用同步的话,显得开销过大,volatile关键字为实例域的同步访问提供了免锁的机制。如果声明一个域为volatile,那么编译器和虚拟机就知道该域是可能被另一个线程并发更新的。再讲到volatile关键字之前我们需要了解一下内存模型的相关概念以及并发编程中的三个特性:原子性,可见性和有序性。

  Java内存模型规定所有的变量都是存在主存当中,每个线程都有自己的工作内存。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。

  执行线程必须先在自己的工作线程中对变量i所在的缓存行进行赋值操作,然后再写入主存当中。而不是直接将数值3写入主存当中。

  对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。

  语句2实际上包含2个操作,它先要去读取x的值,再将x的值写入工作内存,虽然读取x的值以及 将x的值写入工作内存 这2个操作都是原子性操作,但是合起来就不是原子性操作了。

  同样的,x++和 x = x+1包括3个操作:读取x的值,进行加1操作,写入新的值。

  也就是说,只有简单的读取、赋值(而且必须是将数字赋值给某个变量,变量之间的相互赋值不是原子操作)才是原子操作。

  

  另外这个包还包含AtomicBoolean,AtomicLong和AtomicReference这些原子类仅供开发并发工具的系统程序员使用,应用程序员不应该使用这些类。

  可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果。另一个线程马上就能看到。

  当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,所以对其他线程是可见的,当有其他线程需要读取时,它会去内存中读取新值。

  而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。

  在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。

  可以通过volatile关键字来保证一定的“有序性”。另外可以通过synchronized和Lock来保证有序性,很显然,synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。

  一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

  很多人在中断线程时可能都会采用这种标记办法。但是事实上,这段代码会完全运行正确么?即一定会将线程中断么?不一定,也许在大多数时候,这个代码能够把线程中断,但是也有可能会导致无法中断线程(虽然这个可能性很小,但是只要一旦发生这种情况就会造成死循环了)。

  为何有可能导致无法中断线程?每个线程在运行过程中都有自己的工作内存,那么线在运行的时候,会将stop变量的值拷贝一份放在自己的工作内存当中。那么当线更改了stop变量的值之后,但是还没来得及写入主存当中,线转去做其他事情了,那么线由于不知道线对stop变量的更改,因此还会一直循环下去。

  我们知道volatile关键字保证了操作的可见性,但是volatile能保证对变量的操作是原子性吗?

  这段代码每次运行结果都不一致,都是一个小于10000的数字,在前面已经提到过,自增操作是不具备原子性的,它包括读取变量的原始值、进行加1操作、写入工作内存。那么就是说自增操作的三个子操作可能会分割开执行。

  假如某个时刻变量inc的值为10,线对变量进行自增操作,线先读取了变量inc的原始值,然后线被阻塞了;然后线对变量进行自增操作,线也去读取变量inc的原始值,由于线只是对变量inc进行读取操作,而没有对变量进行修改操作,所以不会导致线的工作内存中缓存变量inc的缓存行无效,所以线会直接去主存读取inc的值,发现inc的值时10,然后进行加1操作,并把11写入工作内存,最后写入主存。然后线操作,由于已经读取了inc的值,注意此时在线的工作内存中inc的值仍然为10,所以线写入工作内存,最后写入主存。那么两个线程分别进行了一次自增操作后,inc只增加了1。

  自增操作不是原子性操作,而且volatile也无法保证对变量的任何操作都是原子性的。

  在前面提到volatile关键字能禁止指令重排序,所以volatile能在一定程度上保证有序性。

  当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;

  在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。

  synchronized关键字是防止多个线程同时执行一段代码,那么就会很影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized,但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。通常来说,使用volatile必须具备以下2个条件:

  第一个条件就是不能是自增自减等操作,上文已经提到volatile不保证原子性。

  这种方式限制了范围的状态变量,因此将 lower 和 upper 字段定义为 volatile 类型不能够充分实现类的线程安全,从而仍然需要使用同步。否则,如果凑巧两个线程在同一时间使用不一致的值执行 setLower 和 setUpper 的话,则会使范围处于不一致的状态。例如,如果初始状态是 (0, 5),同一时间内,线程 A 调用 setLower(4) 并且线程 B 调用 setUpper(3),显然这两个操作交叉存入的值是不符合条件的,那么两个线程都会通过用于保护不变式的检查,使得最后的范围值是 (4, 3),这显然是不对的。

  其实就是要保证操作的原子性就可以使用volatile,使用volatile主要有两个场景:

  很可能会从循环外部调用 shutdown() 方法 —— 即在另一个线程中 —— 因此,需要执行某种同步来确保正确实现 shutdownRequested 变量的可见性。然而,使用 synchronized 块编写循环要比使用volatile 状态标志编写麻烦很多。由于 volatile 简化了编码,并且状态标志并不依赖于程序内任何其他状态,因此此处非常适合使用 volatile。

  在这里使用volatile会或多或少的影响性能,但考虑到程序的正确性,牺牲这点性能还是值得的。

  DCL优点是资源利用率高,第一次执行getInstance时单例对象才被实例化,效率高。缺点是第一次加载时反应稍慢一些,在高并发环境下也有一定的缺陷,虽然发生的概率很小。

  DCL虽然在一定程度解决了资源的消耗和多余的同步,线程安全等问题,但是他还是在某些情况会出现失效的问题,也就是DCL失效,在《java并发编程实践》一书建议用以下的代码(静态内部类单例模式)来替代DCL:

  与锁相比,Volatile 变量是一种非常简单但同时又非常脆弱的同步机制,它在某些情况下将提供优于锁的性能和伸缩性。如果严格遵循 volatile 的使用条件即变量真正独立于其他变量和自己以前的值 ,在某些情况下可以使用 volatile 代替 synchronized 来简化代码。然而,使用 volatile 的代码往往比使用锁的代码更加容易出错。本文介绍了可以使用 volatile 代替 synchronized 的最常见的两种用例,其他的情况我们最好还是去使用synchronized 。

  1、volatile的作用相比Sychronized(重量级锁,对系统性能影响较大),volatile提供了另一种解决可见性和有序性问题的方案。对于原子性,需要强调一点,也是大家容易误解的一点:对vo...博文来自:devotion

  volatile影响编译器编译的结果,volatile指出变量是随时可能发生变化的,与volatile变量有关的运算,不要进行编译优化,以免出错,(VC++在产生release版可执行码时会进行编译优...博文来自:Fly

  我们知道volatile关键字的作用是保证变量在多线程之间的可见性,它是current包的核心,没有volatile就没有这么多的并发类给我们使用。本文详细解读一下volat...博文来自:DoubleWeiWei001

  此文乃《java并发编程的艺术》读书笔记。购买链接同时参考文章:Java理论与实践:正确使用Volatile变量定义java编程语言允许线程之间共享变量,为了保证共享变量能够被准确和一致的更新,那么线...博文

  volatile关键字的作用保证内存的可见性防止指令重排注意:volatile并不保证原子性可见性原理volatile保证可见性的原理是在每次访问变量时都会进行一次刷新,因此每次访问都是主内存中最新的...博文来自:热爱我的热爱

  volatile在转成汇编的时候,会加一条lock指令前缀,该指令的作用:1.对变量值的改动会把引起处理器的缓存刷新到主内存2.导致其他处理器的缓存会无效...博文

  volatile的使用条件Volatile变量具有 synchronized 的可见性特性,但是不具备原子性。这就是说线程能够自动发现volatile变量的最新值。Volatile变量可用于提供线程安...博文来自:如果能从来,必将好好学习,天天向上

  远程代理:远程代理就好比“远程对象的本地代表”。所谓远程对象,是一种对象,活在不同的java虚拟机堆中(更一般的说法是,在不同的地址空间运行的远程对象)。所谓的本地代表,是一种可以由本地方法调用的对象...博文来自:pipisky2006的专栏

  原文:变量提供了线程的可见性,并不能保证线程安全性和原子性。什么是线程的...博文来自:jinfeiteng2008的专栏

  volatile关键字在java多线程中有着比较重要作用,volatile主要作用是可以保持变量在多线程中是实时可见的,是java中提供的最轻量的同步机制。可见性在Java的内存模型中所有的的变量(这...博文来自:nethackatschool的专栏

  在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值...博文来自:天元喜羊羊的博客

  volatile的使用volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码...博文来自:kingofbird的专栏

  找了很多资料,包括《java并发编程实战》,综合一下各家的说法就是:volatile让变量每次在使用的时候,都从主存中取。而不是从各个线程的“工作内存”。volatile具有synchronized关...博文来自:imzoer的专栏

  CPU的术语定义volatile是轻量级的synchronized,比之执行成本更低,因为它不会引起线程的上下文切换,它在多处理器开发中保证了共享变量的“可见性”,“可见性”的意思是当一个线程修改一个...博文来自:行走的笔记

  目录JVM内存结构JMM(JavaMemoryModel)主内存和工作内存JMM怎么解决原子性、可见性、有序性的问题?原子性保障可见性有序性volatile如何保证可见性?什么是指令重排序?内存屏障v...博文来自:白与黑的专栏

  Volatile可见性①基本概念:线程之间的可见性,一个线程修改的状态对另一个线程时可见的,也就是一个线程修改的结果,另一个线程马上就能看到。②实现原理:cpu緩存按照读取顺序与CPU结合的紧密程度,...博文来自:huangwei18351的博客

  Java语言提供了volatile,在某些情况下比锁更加方便。如果一个字段被声明成volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的。  那volatile是如何实现可见性...博文来自:yanghan1222的博客

  volatile简介:在Java的多线程中,允许线程访问共享变量,但是为了保证共享变量被准确,一致的更新,需要添加排它锁确保每次更新只有一个线程单独获取它。在某些情况下,Java提供了volatile...博文来自:的博客

  我们都知道,Java关键字volatile的作用1、内存可见性2、禁止指令重排序可见性是指,在多线程环境,共享变量的操作对于每个线程来说,都是内存可见的,也就是每个线程获取的volatile变量都是最...博文来自:ljheee的博客

  volatile时轻量级的synchronized,它在多处理器开发中保证了数据的读的一致性,意思就是当一个线程修改一个共享变量时,另外一个线程能读到这个共享变量的值。如果volatile变量修饰符使...博文来自:的博客

  在java并发编程中,一定绕不开volatile、synchronized和lock几个关键字,其中volatile关键字是用来解决共享变量(类成员变量、类的静态成员变量等)的可见性问题的,非共享变量...博文来自:nch_ren的博客

  百度: 1.内存可见性; 2.禁止指令重排序。 这么底层的专业解释谁能看得懂? 换个思路: redis应该大部分人都用过,当一个查询很频繁的时候,可以把数据缓存到redis, 这样查询直接走缓存,不查论坛

  读写volatile变量就像是访问一个同步块一样,是原子的且是可见的,总是能访问到最新的值。其底层使用了Lock前缀和内存屏障保证可见性。...博文来自:钟绍威

  Java语言规范第3版中对volatile的定义如下:Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。Java语言提供了volati...博文来自:许喜乐

  参数书籍:《Java性能优化权威指南》、《Java并发编程实战》作用:  volatile字段值在所有的线程和CPU缓存中必须保持同步。(参考Java内存模型)  简单讲,你读取的volatile字段...博文来自:zhjali123的专栏

  摘要:Java的volatile关键字对效率的影响Java关键字用于将一个变量标记为“存储在内存中的变量”。更准确的说,意思就是每一次对volatile标记的变量进行读取的时候,都是直接从电脑的主内存...博文来自:一个人的博客

  Volatile定义与原理Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该通过排它锁单独获取这个变量Java语言提供了Violatile来确保多处理开发中,共享变量...博文来自:Moonbow

  1、引言在多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是...博文来自:WuCourage的博客

  用volatile修饰的变量能够保证其对所有线程的可见性,要理解这一点,我们首先需要了解Java的内存模型。1.Java内存模型Java内存模型分为主内存和工作内存。主内存是对所有线程所共享的,此外每...博文来自:java的平凡之路

  volatile关键字的作用和原理关键字作用volatile保证可见性一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:保证了不同线程对这个变量进行操...博文来自:MyHerux的博客

  java中volatile关键字,是可以保证共享变量在线程间具有可见性的,什么意思呢?首先理解可见性,反过来问,为什么共享变量在线程间不是可见的呢(严格说是为什么不总是可见呢)?根据jmm,各个线程对...博文来自:zkl2001_的博客

  Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上...博文来自:training2007的专栏

  1、引言在多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是...博文来自:eff666的博客

  volatile这个关键字可能很多朋友都听说过,或许也都用过。在Java5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果。在Java5之后,volatile关键字才得以重获...博文来自:西楚小羽的专栏

  volatile实现原理一、首先volatile不同于其他的synchronized之类的锁,它是一种轻量级的锁,他加锁是加在变量上的,而且是基于JMM的基础上,在volatile修饰的共享变量进行写...博文来自:王伟的博客

  volatile关键字在c、java中都有,用于修饰变量,例如privatevolatileinti;它在多处理器开发环境中保证了共享变量的“可见性”,即当一个线程修改一个共享变量时,另外一个变量能读...博文来自:l577217的博客

  volatile是Java提供的一种轻量级的同步机制,同synchronized相比,volatile更轻量级,在访问volatile变量时不会执行加锁操作,因此也就不会使执行的线程阻塞。...博文来自:k393393的博客

  原文地址:我们都知道synchronized是一个重量级的锁,虽然JVM对它做了很多优化,而下面介绍的vola...博文来自:skyxuyan的专栏

  提到volatile和synchronized的时候不得不提到的一个东西就是JMM(javaMemoryModel)java内存模型。因为在并发的过程中经常要处理一些可见性、原子性、有序性的问题。并发...博文来自:world6的博客

  jquery/js实现一个网页同时调用多个倒计时(最新的)nn最近需要网页添加多个倒计时. 查阅网络,基本上都是千遍一律的不好用. 自己按需写了个.希望对大家有用. 有用请赞一个哦!nnnn//jsn...博文来自:Websites

  :您好,你后面说的这个发送短信的方式来获取手机号码,有点不太理解,可否留给联系方式,想请教一下,谢谢!

本文链接:http://guidoon.com/zhixingkaixiao/198.html