深入解析Java AtomicInteger原子类型

在进行并发编程的时候我们需要确保程序在被多个线程并发访问时可以得到正确的结果,也就是实现线程安全。线程安全的定义如下:

当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么这个类就是线程安全的。

举个线程不安全的例子。假如我们想实现一个功能来统计网页访问量,你可能想到用count++ 来统计访问量,但是这个自增操作不是线程安全的。count++ 可以分成三个操作:

  1. 获取变量当前值
  2. 给获取的当前变量值+1
  3. 写回新的值到变量

假设count的初始值为10,当进行并发操作的时候,可能出现线程A和线程B都进行到了1操作,之后又同时进行2操作。A先进行到3操作+1,现在值为11;注意刚才AB获取到的当前值都是10,所以B执行3操作后,count的值依然是11。这个结果显然不符合我们的要求。

所以我们需要用本篇的主角—— AtomicInteger 来保证线程安全。

AtomicInteger 的源码如下:

package java.util.concurrent.atomic;
import sun.misc.Unsafe;public class AtomicInteger extends Number implements java.io.Serializable {private static final long serialVersionUID = 6214790243416807050L;// setup to use Unsafe.compareAndSwapInt for updatesprivate static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;static {try {valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }}private volatile int value;public AtomicInteger(int initialValue) {value = initialValue;}public AtomicInteger() {}public final int get() {return value;}public final void set(int newValue) {value = newValue;}public final void lazySet(int newValue) {unsafe.putOrderedInt(this, valueOffset, newValue);}public final int getAndSet(int newValue) {for (;;) {int current = get();if (compareAndSet(current, newValue))return current;}}public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}public final boolean weakCompareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}public final int getAndIncrement() {for (;;) {int current = get();int next = current + 1;if (compareAndSet(current, next))return current;}}public final int getAndDecrement() {for (;;) {int current = get();int next = current - 1;if (compareAndSet(current, next))return current;}}public final int getAndAdd(int delta) {for (;;) {int current = get();int next = current + delta;if (compareAndSet(current, next))return current;}}public final int incrementAndGet() {for (;;) {int current = get();int next = current + 1;if (compareAndSet(current, next))return next;}}public final int decrementAndGet() {for (;;) {int current = get();int next = current - 1;if (compareAndSet(current, next))return next;}}public final int addAndGet(int delta) {for (;;) {int current = get();int next = current + delta;if (compareAndSet(current, next))return next;}}public String toString() {return Integer.toString(get());}public int intValue() {return get();}public long longValue() {return (long)get();}public float floatValue() {return (float)get();}public double doubleValue() {return (double)get();}}

我们先看原子整型类中定义的属性

   // setup to use Unsafe.compareAndSwapInt for updatesprivate static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;static {try {valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }}

Unsafe是JDK内部的工具类,主要实现了平台相关的操作。下面内容引自

sun.misc.Unsafe是JDK内部用的工具类。它通过暴露一些Java意义上说“不安全”的功能给Java层代码,来让JDK能够更多的使用Java代码来实现一些原本是平台相关的、需要使用native语言(例如C或C++)才可以实现的功能。该类不应该在JDK核心类库之外使用。

Unsafe的具体实现跟本篇的目标关联不大,你只要知道这段代码是为了获取value在堆内存中的偏移量就够了。偏移量在AtomicInteger中很重要,原子操作都靠它来实现。

Value的定义和volatile

AtomicInteger 本身是个整型,所以最重要的属性就是value,我们看看它是如何声明value的

 private volatile int value;

我们看到value使用了volatile修饰符,那么什么是volatile呢?

volatile相当于synchronized的弱实现,也就是说volatile实现了类似synchronized的语义,却又没有锁机制。它确保对volatile字段的更新以可预见的方式告知其他的线程。

volatile包含以下语义:

  1. Java 存储模型不会对valatile指令的操作进行重排序:这个保证对volatile变量的操作时按照指令的出现顺序执行的。
  2. volatile变量不会被缓存在寄存器中(只有拥有线程可见)或者其他对CPU不可见的地方,每次总是从主存中读取volatile变量的结果。也就是说对于volatile变量的修改,其它线程总是可见的,并且不是使用自己线程栈内部的变量。也就是在happens-before法则中,对一个valatile变量的写操作后,其后的任何读操作理解可见此写操作的结果。

简而言之volatile 的作用是当一个线程修改了共享变量时,另一个线程可以读取到这个修改后的值。在分析AtomicInteger 源码时,我们了解到这里就足够了。

用CAS操作实现安全的自增

AtomicInteger中有很多方法,例如incrementAndGet() 相当于i++ 和getAndAdd() 相当于i+=n 。从源码中我们可以看出这几种方法的实现很相似,所以我们主要分析incrementAndGet() 方法的源码。

源码如下:

 public final int incrementAndGet() {for (;;) {int current = get();int next = current + 1;if (compareAndSet(current, next))return next;}}public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}

incrementAndGet() 方法实现了自增的操作。核心实现是先获取当前值和目标值(也就是value+1),如果compareAndSet(current, next) 返回成功则该方法返回目标值。那么compareAndSet是做什么的呢?理解这个方法我们需要引入CAS操作。

在大学操作系统课程中我们学过独占锁和乐观锁的概念。独占锁就是线程获取锁后其他的线程都需要挂起,直到持有独占锁的线程释放锁;乐观锁是先假定没有冲突直接进行操作,如果因为有冲突而失败就重试,直到操作成功。其中乐观锁用到的机制就是CAS,Compare and Swap。

AtomicInteger 中的CAS操作就是compareAndSet(),其作用是每次从内存中根据内存偏移量(valueOffset)取出数据,将取出的值跟expect 比较,如果数据一致就把内存中的值改为update。

这样使用CAS就保证了原子操作。其余几个方法的原理跟这个相同,在此不再过多的解释。

没看AtomicInteger 源码之前,我认为其内部是用synchronized 来实现的原子操作。查阅资料后发现synchronized 会影响性能,因为Java中的synchronized 锁是独占锁,虽然可以实现原子操作,但是这种实现方式的并发性能很差。

总结

总结一下,AtomicInteger 中主要实现了整型的原子操作,防止并发情况下出现异常结果,其内部主要依靠JDK 中的unsafe 类操作内存中的数据来实现的。volatile 修饰符保证了value在内存中其他线程可以看到其值得改变。CAS操作保证了AtomicInteger 可以安全的修改value 的值。

from: https://www.cnblogs.com/rever/p/8215743.html

深入解析Java AtomicInteger 原子类型相关推荐

  1. eclipse java转class_Eclipse中的Java项目:无法解析java.lang.Object类型。 它是从所需的.class文件间接引用的...

    Eclipse中的Java项目:无法解析java.lang.Object类型. 它是从所需的.class文件间接引用的 在Eclipse中导入项目后,我收到以下错误: 无法解析java.lang.Ob ...

  2. 深度解析Java可变参数类型以及与数组的区别

    这篇文章主要介绍了Java方法的可变参数类型,通过实例对Java中的可变参数类型进行了较为深入的分析,需要的朋友可以参考下. Java方法中的可变参数类型是一个非常重要的概念,有着非常广泛的应用.本文 ...

  3. Java可变参数类型实例

    可变参数:         Java1.5增加了新特性:可变参数:适用于参数个数不确定,类型确定的情况,java把可变参数当做数组处理. 可变的参数类型,也称为不定参数类型.英文缩写是varargus ...

  4. java中jdk1.8,做forEach,需要把外面的变量赋值如int,long,boolean,需要外面定义原子类型的Atomic类型,AtomicBoolean,AtomicInteger

    2022-01-05 星期三 1414 by:enAn java中jdk1.8,做forEach,需要把外面的变量赋值如int,long,boolean,需要外面定义原子类型的Atomic,如:Ato ...

  5. 聊聊高并发(二十)解析java.util.concurrent各个组件(二) 12个原子变量相关类

    这篇说说java.util.concurrent.atomic包里的类,总共12个,网上有很多文章解析这几个类,这里挑些重点说说. 这12个类可以分为三组: 1. 普通类型的原子变量 2. 数组类型的 ...

  6. Java并发23:Atomic系列-普通原子类型AtomicXxxx学习笔记

    [超级链接:Java并发学习系列-绪论] [系列概述: Java并发22:Atomic系列-原子类型整体概述与类别划分] 本章主要对普通原子类型进行学习. 1.普通原子类型 在java.util.co ...

  7. 原子类型:AtomicInteger认识

    文章目录 1 原子类型介绍 2 AtomicInteger性能对比(了解) 3 AtomicInteger基本用法 3.1 AtomicInteger的创建(构造方法) 3.2 AtomicInteg ...

  8. 解析java中的字面量和字符类型

    解析java中的字面量和字符类型 1.字面量含义 固定不变的量,我们人为所给的一些数据.例如77和88都是整型字面量,1.88和1.99F是浮点型字面量,'中'是字符型字面量,"dfguyf ...

  9. 转 : 深入解析Java锁机制

    深入解析Java锁机制 https://mp.weixin.qq.com/s?__biz=MzU0OTE4MzYzMw%3D%3D&mid=2247485524&idx=1&s ...

最新文章

  1. 赠书 | 人工智能识万物:卷积神经网络的前世今生
  2. python为什么要使用闭包
  3. 块级作用域(es5没闭包-有闭包-let对比)
  4. 常用linux下网络相关命令
  5. 合奏:机器学习中唯一(几乎)免费的午餐
  6. wince6下usb摄像头(UVC)使用指南
  7. Flowable 数据库表结构 ACT_HI_ATTACHMENT
  8. html如何创建关键帧动画旋转,[教程] 用 CSS3 Animations(动画) 和 Keyframes(关键帧) 创建简单的网页动画 – CSS3 实现弹跳球动画...
  9. TypeScript学习笔记(第一天)------创建简单的web应用
  10. 关于SuperMap的ISManager访问权限问题
  11. DICOM worklist入门一
  12. vue+springboot传数据到数据库一直报错500解决方案
  13. html中加入标题居中,在html标题标记中居中的div元素
  14. 不规则四面体知道六边的体积公式
  15. 离散时间傅里叶变换(DTFT)与离散傅里叶级数(DFS)
  16. C++基础入门(第一篇)
  17. 什么是域名服务器作用是啥,域名服务器的作用是什么?域名服务器原理及流程...
  18. html 标题设置链接 博客,从今天开始,拿起VuePress打造属于自己的专属博客
  19. mysql修改时区为utc
  20. 破解wifi密码 暴力破解 保姆式教学

热门文章

  1. Spring Boot AutoConfiguration注解@ConditionalXXXX之前生今世
  2. CORS support in Spring Framework--官方
  3. 【图数据库】史上超全面的Neo4j使用指南
  4. 【深度学习下一大突破】吴恩达对话 Hinton、Bengio、Goodfellow(视频)
  5. 蚂蚁金服发布「定损宝」,推动图像定损技术在车险领域的应用
  6. GBDT(Gradient Boosting Decision Tree
  7. Google的深度学习强在哪?谷歌首席科学家说了这些奇妙特性
  8. java 自定义validate_Golang-03 自定义validator,实现java注解功能-Go语言中文社区
  9. jvm性能调优 - 10白话年轻代数据晋升老年代规则及老年代回收算法
  10. MySQL-Btree索引和Hash索引初探