热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java中volatile关键字

Part1volatile,用更低的代价替代同步为什么使用volatile比同步代价更低?同步的代价,主要由其覆盖范围决定,如果可以降低同步的覆盖范围,则可以大幅提升程序性能.

Part 1 volatile,用更低的代价替代同步


为什么使用volatile比同步代价更低?

同步的代价, 主要由其覆盖范围决定, 如果可以降低同步的覆盖范围, 则可以大幅提升程序性能.
而volatile的覆盖范围仅仅变量级别的. 因此它的同步代价很低.


volatile原理是什么?

volatile的语义, 其实是告诉处理器, 不要将我放入工作内存, 请直接在主存操作我.(工作内存详见java内存模型)因此, 当多核或多线程在访问该变量时, 都将直接操作主存, 这从本质上, 做到了变量共享.


volatile的有什么优势?

1, 更大的程序吞吐量
2, 更少的代码实现多线程
3, 程序的伸缩性较好
4, 比较好理解, 无需太高的学习成本


volatile有什么劣势?

1, 容易出问题
2, 比较难设计


Part 2 volatile运算存在脏数据问题

volatile仅仅能保证变量可见性, 无法保证原子性(volatile很容易被误用来进行原子性操作)。


//volatile的race condition示例:
public class TestRaceCondition {private volatile int i = 0;public void increase() {i++;}public int getValue() {return i;}
}

当多线程执行increase方法时, 是否能保证它的值会是线性递增的呢?
        答案是否定的. 原因:这里的increase方法, 执行的操作是i&#43;&#43;, 即 i &#61; i &#43; 1;针对i &#61; i &#43; 1, 在多线程中的运算, 本身需要改变i的值.如果, 在i已从内存中取到最新值, 但未与1进行运算, 此时其他线程已数次将运算结果赋值给i.则当前线程结束时, 之前的数次运算结果都将被覆盖.即, 执行100次increase, 可能结果是 <100.一般来说, 这种情况需要较高的压力与并发情况下, 才会出现.
如何避免这种情况?
        一是, 加上同步. 这种方法, 无疑将大大降低程序性能, 且违背了volatile的初衷. 或者是, 使用硬件原语(CAS), 实现非阻塞算法. 从CPU原语上,  支持变量级别的低开销同步.

CPU原语-比较并交换(CompareAndSet),实现非阻塞算法


什么是CAS?
        CAS是现代CPU提供给并发程序使用的原语操作. 不同的CPU有不同的使用规范.在 Intel 处理器中&#xff0c;比较并交换通过指令的 cmpxchg 系列实现。PowerPC 处理器有一对名为“加载并保留”和“条件存储”的指令&#xff0c;它们实现相同的目地&#xff1b;MIPS 与 PowerPC 处理器相似&#xff0c;除了第一个指令称为“加载链接”。CAS 操作包含三个操作数 —— 内存位置&#xff08;V&#xff09;、预期原值&#xff08;A&#xff09;和新值(B)
什么是非阻塞算法?

        一个线程的失败或挂起不应该影响其他线程的失败或挂起.这类算法称之为非阻塞(nonblocking)算法。对比阻塞算法: 如果有一类并发操作, 其中一个线程优先得到对象监视器的锁, 当其他线程到达同步边界时, 就会被阻塞. 直到前一个线程释放掉锁后, 才可以继续竞争对象锁.(当然,这里的竞争也可是公平的, 按先来后到的次序)


CAS 原理:
        我认为位置 V 应该包含值 A&#xff1b;如果包含该值&#xff0c;则将 B 放到这个位置&#xff1b;否则&#xff0c;不要更改该位置&#xff0c;只告诉我这个位置现在的值即可。

CAS使用示例(jdk 1.5 并发包 AtomicInteger类分析)

/*** Atomically sets to the given value and returns the old value.** &#64;param newValue the new value* &#64;return the previous value*/public final int getAndSet(int newValue) {for (;;) {int current &#61; get();if (compareAndSet(current, newValue))return current;}}public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}
这个方法是, AtomicInteger类的常用方法, 作用是, 将变量设置为指定值, 并返回设置前的值. 它利用了cpu原语compareAndSet来保障值的唯一性. 另, AtomicInteger类中, 其他的实用方法, 也是基于同样的实现方式. 比如 getAndIncrement, getAndDecrement, getAndAdd等等.

CAS语义上存在的"ABA 问题"


什么是ABA问题?

        假设, 第一次读取V地址的A值, 然后通过CAS来判断V地址的值是否仍旧为A, 如果是, 就将B的值写入V地址,覆盖A值. 但是, 语义上, 有一个漏洞, 当第一次读取V的A值, 此时, 内存V的值变为B值, 然后在未执行CAS前, 又变回了A值. 此时, CAS再执行时, 会判断其正确的, 并进行赋值. 这种判断值的方式来断定内存是否被修改过, 针对某些问题, 是不适用的.


为了解决这种问题, jdk 1.5并发包提供了AtomicStampedReference(有标记的原子引用)类, 通过控制变量值的版本来保证CAS正确性.其实, 大部分通过值的变化来CAS, 已经够用了


jdk1.5原子包介绍(基于volatile)


包的特色如下&#xff0c;具体使用, 详解java doc。
1, 普通原子数值类型AtomicInteger, AtomicLong提供一些原子操作的加减运算.
2, 使用了解决脏数据问题的经典模式-"比对后设定", 即 查看主存中数据是否与预期提供的值一致,如果一致,才更新.
3, 使用AtomicReference可以实现对所有对象的原子引用及赋值.包括Double与Float,但不包括对其的计算.浮点的计算,只能依靠同步关键字或Lock接口来实现了.
4, 对数组元素里的对象,符合以上特点的, 也可采用原子操作.包里提供了一些数组原子操作类AtomicIntegerArray, AtomicLongArray等等.
5, 大幅度提升系统吞吐量及性能.

volatile可以用在任何变量前面&#xff0c;但不能用于final变量前面&#xff0c;因为final型的变量是禁止修改的。也不存在线程安全的问题。

本文来源



推荐阅读
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 本文说明了Linux系统的配置文件,在多用户、多任务环境中,配置文件控制用户权限、系统应用程序、守护进程、服务和其它管理任务。这些任务包括管理用户帐号、 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • 本文详细介绍了如何使用MySQL来显示SQL语句的执行时间,并通过MySQL Query Profiler获取CPU和内存使用量以及系统锁和表锁的时间。同时介绍了效能分析的三种方法:瓶颈分析、工作负载分析和基于比率的分析。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • Hadoop 源码学习笔记(4)Hdfs 数据读写流程分析
    Hdfs的数据模型在对读写流程进行分析之前,我们需要先对Hdfs的数据模型有一个简单的认知。数据模型如上图所示,在NameNode中有一个唯一的FSDirectory类负责维护文件 ... [详细]
  • windows平台使用NSP拦截具体进程的域名解析过程(xFsRedir的代理功能之域名代理)
    byfanxiushu2022-10-17转载或引用请注明原始作者。xFsRedir软件其中之一的功能就是实现了全方位的网络代理,从主机代理,到本地代理 ... [详细]
  • 微服务下的几个难点问题及常见的解决方案
    原文链接:https:cloud.tencent.comdevelopernews1362051背景介绍1.1幂等性定义数学定义在数学里,幂等有 ... [详细]
  • mapreduce原理_MapReduce原理及WordCount实践
    参考链接:https:www.cnblogs.comlaowangcp8961946.html一、MapReduce流程1.1Mapreduce整体流程: ... [详细]
  • 小白轻松使用axis2构建webservice
    引言:使用axis2是来实现webservice接口是比较常见的,就我来说,如果要学一个首次接触东西,简单了解相关基础概念 ... [详细]
author-avatar
牛哥粉丝_对白
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有