`
春花秋月何时了
  • 浏览: 39550 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

同步数据结构之原子类序章

阅读更多

概述

关于Java并发包的原子相关类其实可以在Unsafe后面紧接着进行介绍,因为这些原子类其实只是建立在对Unsafe的调用基础上并没有利用到Java并发包的其他类,出于前面介绍的同步组件在Java并发包的重要性,我才将它们放在了原子类的前面。

Java从JDK 1.5开始提供的java.util.concurrent.atomic原子包给开发人员提供了一组用法简单、性能高效、线程安全的更新单个变量的方法。并且相对于使用传统的synchronized关键字的方式,原子包从使用者层面上看,是一种更加轻量级、细粒度、无锁的保证线程安全的策略。

因为原子包其实采用的是CAS + Volatile关键字的方式实现的这些无锁高效的线程安全机制,而CAS机制实际上是直接借助了当前平台的CPU的相关高效的原子指令来完成的,而在某些平台的CPU架构上原子包在实现的时候在内部还是借助了某种形式的内部锁的思想,所以原子包中的那些看似无锁的线程安全的方法还是具有一定程度的平台相关性,在某些平台上不能绝对保证线程不被阻塞。

 

原子包中的类可以分为5类:

  1. 原子更新标量类:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
  2. 原子更新数组类:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
  3. 原子更新字段类:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater
  4. 原子更新复合类:AtomicMarkableReference,将一个布尔值与引用变量关联起来,可用于标记数据结构中的节点等;AtomicStampedReference,将一个整数值与引用变量关联起来,可用于添加更新版本号解决ABA问题
  5. JDK8新增的高竞争下的累加器及其扩展类:Striped64,LongAccumulator,LongAdder,DoubleAccumulator,DoubleAdder

原子包中的大部分类及其实现方法都包含或利用如下几个重要方法:

  1. get:由于操作的变量被volatile修饰,所以具有读取 volatile 变量的内存效果,总是能够获取到最新的值。
  2. set由于操作的变量被volatile修饰,所以具有写入volatile 变量的内存效果,立即将更新刷新到主存对其他线程可见。 
  3. lazySet即“延迟可见写”,直接使用的Unsafe的putOrderedXXX(Object o, long offset, long x)方法实现,该方法在Unsafe章节已经详细分析过,它能够执行高效的写入,但不保证更改立即对其他线程可见,而且由于在前面加上了store-store内存屏障,所以能够保证前面的写操作不会被重排序到lazySet的后面去。
  4. compareAndSet、getAndIncrement等读写方法compareAndSet是CAS机制的实现方法,保证了操作的原子性,当操作的变量是被volatile修饰的时候,它具备了volatile变量读写操作的内存效果,原子包中操作的属性都是被volatile修饰的,所以原子包中的CAS操作都具有读写volatile变量的内存效果,也就说同时满足了可见性、有序性以及原子性。除此之外,getAndIncrement等其他读写方法都具有相同的内存效果。
  5. weakCompareAndSet我们知道compareAndSet同时具有volatile变量的内存效果,确切是说,它除了能保证对自身变量的读写操作的原子性与有序性、可见性外,还能因为对volatile变量的写操作会在其前后加入相应的内存屏障达到使其前面的其他写和后面的读操作不但不能重排序而且还会跟着变得立即可见,也就是说compareAndSet + volatile同时保证了在该操作前后的其他读写操作的有序性和可见性,即它们满足happen-before规则。而原子包提供的该weakCompareAndSet方法则被称之为不保证有序性的compareAndSet方法,也就是说weakCompareAndSet只保证对自身操作的原子性和可见性,但不保证在该操作前面和后面的其他读写操作的有序性和可见性,即不附带任何的happen-before规则,更通俗的说就是,weakCompareAndSet只保证自身的原子性和可见性,在它前面和后面的其他读写操作可能会被重排序,所以当其他线程能够看到weakCompareAndSet操作的结果时,并不保证一定也能看到在weakCompareAndSet操作前面或者后面的操作的结果。一般来说,weakCompareAndSet都比compareAndSet更加高效,常常用于在程序运行时更新计数器或者统计数据这类无关于其他happens-before的程序中非常有用。另外,weakCompareAndSet操作可能会返回一个虚假的失败结果false,这种失败可能是由于内存冲突导致的,而和预期值(expectedValue)是否和当前的值相等无关。最后,通过查看原子包的源码,你会发现weakCompareAndSet和compareAndSet方法的实现是一模一样的,这是因为weakCompareAndSet是由JVM在运行时通过JIT即时编译的平台相关的手写汇编程序替换执行的,所以只有在运行时通过hsdis插件和JVM参数-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly打印出真正执行的汇编指令才会发现他们的差异(奇怪的是我在测试的时候,发现该测试程序需要循环多次调用才能看到相应的汇编指令)。值得一提的是,在JDK9中,(weak)compareAndSet这两个方法已经被加上了一个@HotSpotIntrinsicCandidate注解,更加明确的表示这两个方法可能( 但不保证 )通过HotSpot VM自己来写汇编或IR编译器来实现该方法以提供性能。

原子类主要被设计为用于实现非阻塞数据结构和相关基础结构类。compareAndSet方法也不是锁的常规替换方法,仅当对象的重要更新限定于单个变量时才使用它。

原子类也不是java.lang.Integer 和相关类的常规替换。原子类没有定义诸如 equals、hashCode 和 compareTo 之类的方法。(因为原子变量是期望可变的,所以对于哈希表的键值来说,它们不是好的选择。)另外,原子包仅为一些常用的类型提供了原子类。例如,并没有表示 byte 的原子类。如果要实现这种不常见类型的原子类,可以使用 AtomicInteger 来保存 byte 值,并进行适当的强制转换。也可以使用 Float.floatToRawIntBits(float) 和 Float.intBitsToFloat(int) 转换来保存 float 类型的值,使用 Double.doubleToRawLongBits(double) 和 Double.longBitsToDouble(long) 转换来保存 double 类型的值。

 

本章节从整体到局部重要方法的形式阐述了Java并发包的atomic原子类的设计思想以及原子类家族的5种成员结构,接下来的章节我将分别对这5种原子类进行分析研究。 

分享到:
评论

相关推荐

    Java并发编程实战

    第1章 简介 1.1 并发简史 1.2 线程的优势 1.2.1 发挥多处理器的强大能力 1.2.2 建模的简单性 1.2.3 异步事件的简化处理 ...第15章 原子变量与非阻塞同步机制 第16章 Java内存模型 附录A 并发性标注 参考文献

    Java 并发编程实战

    前 言 第1章 简介 1.1 并发简史 1.2 线程的优势 1.2.1 发挥多处理器的强大能力 1.2.2 建模的简单性 1.2.3 异步事件的简化处理 ...第15章 原子变量与非阻塞同步机制 第16章 Java内存模型 附录A 并发性标注

    Windows编程循序渐进.part2

    17.3.1 SEH相关数据结构的介绍 314 17.3.2 异常处理链结构图 315 17.3.3 实例:单嵌套异常块演示程序 316 17.3.4 实例:多嵌套异常块演示程序 318 17.3.5 VC++编译器级异常帧结构 320 17.3.6 VC中的顶层异常...

    Windows编程循序渐进.part3

    17.3.1 SEH相关数据结构的介绍 314 17.3.2 异常处理链结构图 315 17.3.3 实例:单嵌套异常块演示程序 316 17.3.4 实例:多嵌套异常块演示程序 318 17.3.5 VC++编译器级异常帧结构 320 17.3.6 VC中的顶层异常...

    Computer-Science-with-iOS:iOS中的计算机科学知识,数据结构,算法的集合-Swift

    数据结构 :link: :books: :woman_and_man_holding_hands: :antenna_bars: :file_folder: NSCache :mobile_phone: iOS知识 弧堆放内存段Memort泄漏并对其进行调试保留周期僵局,竞争状况,读者编写者问题,...

    疯狂内核之——进程管理子系统

    1.1 进程相关数据结构 5 1.1.1 进程的基本信息 6 1.1.2 进程状态 10 1.1.3 TASK_RUNNING状态的进程链表 11 1.1.4 进程间关系 12 1.2 Linux的线程——轻量级进程 15 1.3 进程的创建——do_fork()函数详解 19 1.4 执行...

    matlab计算器log的代码-aewsome-technology-life-growth:技术|生活|成长

    数据结构与算法 剑指 Offer LeetCode 请详见 数据库 Redis MyBatis TODO MySQL Linux Spring 相关 Spring Spring MVC TODO Spring Boot TODO Spring Cloud TODO 设计模式 TODO Docker TODO Kafka TODO Dubbo TODO ...

    C#并行编程高级教程:精通.NET 4 Parallel Extensions中文(第3部分)

    第5章 协调数据结构 5.1 通过汽车和车道理解并发难题 5.1.1 非预期的副作用 5.1.2 竞争条件 5.1.3 死锁 5.1.4 使用原子操作的无锁算法 5.1.5 使用本地存储的无锁算法 5.2 理解新的同步机制 5.3 使用同步原语...

    C#并行编程高级教程:精通.NET 4 Parallel Extensions中文(第一部分)

    第5章 协调数据结构 5.1 通过汽车和车道理解并发难题 5.1.1 非预期的副作用 5.1.2 竞争条件 5.1.3 死锁 5.1.4 使用原子操作的无锁算法 5.1.5 使用本地存储的无锁算法 5.2 理解新的同步机制 5.3 使用同步原语 5.3.1 ...

    C#并行编程高级教程:精通.NET 4 Parallel Extensions中文(第2部分)

    第5章 协调数据结构 5.1 通过汽车和车道理解并发难题 5.1.1 非预期的副作用 5.1.2 竞争条件 5.1.3 死锁 5.1.4 使用原子操作的无锁算法 5.1.5 使用本地存储的无锁算法 5.2 理解新的同步机制 5.3 使用同步原语...

    Java虚拟机

    5.2.6 不恰当数据结构导致内存占用过大 5.2.7 由Windows虚拟内存导致的长时间停顿 5.3 实战:Eclipse运行速度调优 5.3.1 调优前的程序运行状态 5.3.2 升级JDK 1.6的性能变化及兼容问题 5.3.3 编译时间和类加载...

    java进阶13天资料.zip

    day05-迭代器,数据结构,List,Set ,TreeSet集合,Collections工具类 day06-Map集合,HashMapTreeMap,斗地主、图书管理系统,排序算法 day07-异常,线程的创建方式,,线程安全,线程同步 day08-线程状态,volatile关键字...

    分布式系统领域教程pdf

    第11章 分布式数据管理 11.1 基本概念 11.2 可串行性理论 11.3 并发控制 11.3.1 基于锁的并发控制 11.3.2 基于时戳的并发控制 11.3.3 乐观的并发控制 11.4 复制和一致性管理 11.4.1 主站点方法 11.4.2 活动...

    分布式系统设计 [美]jie wu著 高传善 译

    第11章 分布式数据管理 11.1 基本概念 11.2 可串行性理论 11.3 并发控制 11.3.1 基于锁的并发控制 11.3.2 基于时戳的并发控制 11.3.3 乐观的并发控制 11.4 复制和一致性管理 11.4.1 主站点方法 11.4.2 活动...

    java内核源码-JavaCompass:「Java指南针」为你学习Java指明方向。内容涵盖互联网Java工程师所需要掌握的核心知识,涉及J

    数据结构与算法 入门基础 基础数据结构 数组&链表 数组&链表进阶 栈 队列 算法思想 数论&枚举&递归&分治&回溯 排序及其源码实现 贪心&动态规划 高级数据结构 树论基础&二叉树 二叉搜索树&红黑树 BTree Trie树&赫夫曼...

    深入理解_Java_虚拟机 JVM_高级特性与最佳实践

    / 126 5.3.5 选择收集器降低延迟 / 130 5.4 本章小结 / 133 第三部分 虚拟机执行子系统 第6章 类文件结构 / 136 6.1 概述 / 136 6.2 无关性的基石 / 136 6.3 Class类文件的结构 / 138 6.3.1 魔数与Class文件...

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    10.1.9 万类之祖——Object类 250 10.2 子类对象?父类对象? 251 10.2.1 父随子行 251 10.2.2 当构造方法遇到继承 254 10.2.3 记得给类一个无参数的构造方法 255 10.2.4 调用父类中的构造方法 256 10.2.5 ...

    Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)

    10.1.9 万类之祖——Object类 250 10.2 子类对象?父类对象? 251 10.2.1 父随子行 251 10.2.2 当构造方法遇到继承 254 10.2.3 记得给类一个无参数的构造方法 255 10.2.4 调用父类中的构造方法 256 10.2.5 ...

Global site tag (gtag.js) - Google Analytics