Java 从 JDK 1.5 开始提供了 java.util.concurrent.atomic 包(以下简称Atomic包),这个包中的 原子操作类 提供了一种用法简单、性能高效、线程安全地更新一个变量的方式。

因为变量的类型有很多种,所以在 Atomic 包里一共提供了 12个 类,属于以下 4 种类型的原子更新方式:

原子更新基本类型。

AtomicBoolean:原子更新布尔类型。

AtomicInteger:原子更新整型。

AtomicLong:原子更新长整型。

原子更新数组。

AtomicIntegerArray:原子更新整型数组里的元素。

AtomicLongArray:原子更新长整型数组里的元素。

AtomicReferenceArray:原子更新引用类型数组里的元素。

原子更新引用。

AtomicReference:原子更新对象引用。

AtomicMarkableReference:原子更新带有标记位的对象引用。

AtomicStampedReference:原子更新带有版本号的对象引用。

原子更新属性(字段)。

AtomicIntegerFieldUpdater:原子更新volatile修饰的整型的字段的更新器。

AtomicLongFieldUpdater:原子更新volatile修饰的长整型字段的更新器。

AtomicReferenceFieldUpdater:原子更新volatile修饰的引用类型里的字段的更新器。

Atomic 包里的类基本都是使用 Unsafe 实现的包装类。

原子更新基本类型

AtomicBoolean:原子更新布尔类型。

AtomicInteger:原子更新整型。

AtomicLong:原子更新长整型。

以上3个类提供的方法几乎一模一样,所以本节仅以 AtomicInteger 为例进行讲解。

AtomicInteger 的常用方法如下:

int addAndGet(int delta):以原子方式将输入的数值与实例中的值(AtomicInteger 里的 value)相加,并返回结果。

boolean compareAndSet(int expect,int update):如果输入的数值等于预期值,则以原子方式将该值设置为输入的值。

int getAndIncrement():以原子方式将当前值加1,注意,这里返回的是自增前的值。

void lazySet(int newValue):最终会设置成 newValue,使用 lazySet 设置值后,可导致其他线程在之后的一小段时间内还是可以读到旧的值。

int getAndSet(int newValue):以原子方式设置为 newValue 的值,并返回旧值。

示例

public static void main(String[] args) {AtomicInteger ai = new AtomicInteger(2);System.out.println("ai.get() = " + ai.get());System.out.println("ai.addAndGet(5) = " + ai.addAndGet(5));System.out.println("ai.get() = " + ai.get());System.out.println("ai.compareAndSet(ai.get(), 10) = " + ai.compareAndSet(ai.get(), 10));System.out.println("ai.get() = " + ai.get());System.out.println("ai.getAndIncrement() = " + ai.getAndIncrement());System.out.println("ai.get() = " + ai.get());ai.lazySet(8);System.out.println("ai.lazySet(8)");System.out.println("ai.get() = " + ai.get());System.out.println("ai.getAndSet(5) = " + ai.getAndSet(5));System.out.println("ai.get() = " + ai.get());
}

输出

ai.get() = 2
ai.addAndGet(5) = 7
ai.get() = 7
ai.compareAndSet(ai.get(), 10) = true
ai.get() = 10
ai.getAndIncrement() = 10
ai.get() = 11
ai.lazySet(8)
ai.get() = 8
ai.getAndSet(5) = 8
ai.get() = 5

AtomicInteger 的 getAndIncrement()方法:

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

for 循环体的先取得 AtomicInteger 里存储的数值

对 AtomicInteger 的当前数值进行 +1 操作,

关键是调用 compareAndSet 方法来进行原子更新操作,该方法先检查 当前数值是否等于current ?

 等于意味着 AtomicInteger 的值没有被其他线程修改过,则将 AtomicInteger 的当前数值更新成 next的值。如果不等 compareAndSet 方法会返回 false,程序会进入 for 循环重新进行 compareAndSet 操作。

Atomic 包提供了 3 种基本类型的原子更新,但是 Java 的基本类型里还有 char、float 和 double 等。

那么问题来了,如何原子的更新其他的基本类型呢?

Atomic包里的类基本都是使用 Unsafe 实现的,让我们一起看一下Unsafe的源码:

/*** 如果当前数值是expected,则原子的将Java变量更新成x** @return 如果更新成功则返回true*/
public final native boolean compareAndSwapObject(Object o, long offset,Object expected, Object x);public final native boolean compareAndSwapInt(Object o, long offset,int expected, int x);public final native boolean compareAndSwapLong(Object o, long offset,long expected, long x);

综合上述代码,我们可以发现 Unsafe 只提供了 3 种 CAS 方法:compareAndSwapObject、compareAndSwapInt 和 compareAndSwapLong,再看 AtomicBoolean 源码,发现它是先把 Boolean 转换成 整型,再使用 compareAndSwapInt 进行 CAS,所以原子更新 char、float 和 double 变量也可以用类似的思路来实现。

原子更新数组

AtomicIntegerArray:原子更新整型数组里的元素。

AtomicLongArray:原子更新长整型数组里的元素。

AtomicReferenceArray:原子更新引用类型数组里的元素。

以上几个类提供的方法几乎一样,所以仅以 AtomicIntegerArray 为例进行介绍:
AtomicIntegerArray 类主要是提供原子的方式更新数组里的整型。

常用方法如下:

 int addAndGet(int i,int delta):以原子方式将输入值与数组中索引i的元素相加。boolean compareAndSet(int i,int expect,int update):如果当前值等于预期值,则以原子方式将数组位置i的元素设置成update值。

示例

public static void main(String[] args) {int[] value = new int[]{1, 2};AtomicIntegerArray ai = new AtomicIntegerArray(value);System.out.println("ai.getAndSet(0, 3)");ai.getAndSet(0, 3);System.out.println("ai.get(0) = " + ai.get(0));System.out.println("value[0] = " + value[0]);ai.compareAndSet(1, 2, 5);System.out.println("ai.compareAndSet(1, 2, 5)");System.out.println("ai.get(1) = " + ai.get(1));
}

输出

ai.getAndSet(0, 3)
ai.get(0) = 3
value[0] = 1
ai.compareAndSet(1,2,5)
ai.get(1) = 5

注意:数组value通过构造方法传递进去,然后AtomicIntegerArray会将当前数组复制一份,所以当AtomicIntegerArray对内部的数组元素进行 修改 时,不会影响传入的数组。

原子更新引用

AtomicReference:原子更新对象引用。

AtomicMarkableReference:原子更新带有标记位的对象引用。

AtomicStampedReference:原子更新带有版本号的对象引用。该类将整数值与引用关联起来,可用于原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA问题。

我们以AtomicReference为例进行介绍。

示例

public class AtomicReferenceTest {public static AtomicReference<User> atomicUserRef = newAtomicReference<User>();public static void main(String[] args) {User user = new User("103style", 20);atomicUserRef.set(user);System.out.println("atomicUserRef.get() = " + atomicUserRef.get().toString());User updateUser = new User("xiaoke", 22);atomicUserRef.compareAndSet(user, updateUser);System.out.println("atomicUserRef.compareAndSet(user, updateUser);");System.out.println("atomicUserRef.get() = " + atomicUserRef.get().toString());}static class User {private String name;private int age;public User(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}@Overridepublic String toString() {return "name='" + name + ", age=" + age;}}
}

输出

atomicUserRef.get() = name='103style, age=20
atomicUserRef.compareAndSet(user, updateUser);
atomicUserRef.get() = name='xiaoke, age=22

原子更新属性(字段)

AtomicIntegerFieldUpdater:原子更新volatile修饰的整型的字段的更新器。

AtomicLongFieldUpdater:原子更新volatile修饰的长整型字段的更新器。

AtomicReferenceFieldUpdater:原子更新volatile修饰的引用类型里的字段的更新器。

要想原子地更新字段类需要两步:

 因为原子更新字段类都是抽象类,每次使用的时候必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。更新类的字段(属性)必须使用public volatile修饰符。

我们以AstomicIntegerFieldUpdater 为例进行讲解。

示例

public class AtomicIntegerFieldUpdaterTest {public static void main(String[] args) {// 创建原子更新器,并设置需要更新的对象类和对象的属性AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater.newUpdater(User.class, "age");// 设置柯南的年龄是10岁User conan = new User("conan", 10);// 柯南长了一岁,但是仍然会输出旧的年龄System.out.println(a.getAndIncrement(conan));// 输出柯南现在的年龄System.out.println(a.get(conan));}public static class User {public volatile int age;private String name;public User(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}}
}

输出

10
11

了解更多欢迎点赞关注的哟!!!

Java中12个原子操作类相关推荐

  1. Java中Date和Calender类的使用方法

    查看文章     Java中Date和Calender类的使用方法 2009-10-04 20:49 Date和Calendar是Java类库里提供对时间进行处理的类,由于日期在商业逻辑的应用中占据着 ...

  2. 《Java并发编程的艺术》——Java中的并发工具类、线程池、Execute框架(笔记)

    文章目录 八.Java中的并发工具类 8.1 等待多线程完成的CountDownLatch 8.2 同步屏障CyclicBarrier 8.2.1 CyclicBarrier简介 8.2.2 Cycl ...

  3. Java中通过代理对类进行修改

    JAVA中的静态代理.动态代理及JDK proxy和CGLIB.Javassist.ASM实践 简介 Java中对已经有的类进行修改,改变或调整其执行,这可以通过代理来实现.Java的class文件是 ...

  4. java中的starts_Java Math类静态double nextAfter(double starts,double direction)示例

    java中的starts 数学类静态double nextAfter(双向启动,双向) (Math Class static double nextAfter(double starts , doub ...

  5. setyear java_如何在Java中创建不可变类

    如果对象在构造后无法更改,则该对象是不可变的.不可变对象不会以任何方式暴露其他对象来修改其状态; 对象的字段仅在构造函数内初始化一次,并且永远不会再次更改. 在本文中,我们将定义在Java中创建不可变 ...

  6. java创建一个不可变对象_如何在Java中创建不可变类?

    java创建一个不可变对象 Today we will learn about the immutable class in Java. What are immutable classes? The ...

  7. 《Java并发编程的艺术》读后笔记-Java中的并发工具类(第八章)

    文章目录 <Java并发编程的艺术>读后笔记-Java中的并发工具类(第八章) 1.等待多线程完成的CountDownLatch 2.同步屏障CyclicBarrier 2.1 Cycli ...

  8. Java中的网络编程类(TCPUDP)

    Java中的网络编程类 n Java.net包 – TCP协议 URL URLConnection Socket ServerSocket – UDP协议 DatagramPacket Datagra ...

  9. 【JAVA系列】Java中的包、类的继承、多态、抽象类与接口

    文章目录 前言 一.包及访问权限 1.什么是包? 2.如何导入包? 3.JDK中常见的包 4.包的访问控制权限 二.继承 1.继承的基本概念 2.继承时方法调用顺序 3.super和this关键字 4 ...

最新文章

  1. 如何从eclipse迁移到idea
  2. JAVA Bean和XML之间的相互转换 - XStream简单入门
  3. PHP probuf详细步骤_go+protobuf+php简单示例
  4. Spring MVC自定义验证注释
  5. html5 canvas获取坐标,HTML5 canvas坐标
  6. 云效发布策略指南|滚动、分批、灰度怎么选?
  7. 云端计算机可以玩游戏么,手机掌上云电脑是什么?为什么可以玩PC游戏?
  8. should be mapped with insert=false update=false
  9. div常用效果方法-transform
  10. Shiro Spring 集成xml配置
  11. 想多赚钱!程序员如何把副业搞得风生水起?
  12. SOLIDWORKS软件二十四年来的进化发展史
  13. Vue的内置指令:v-if和v-show的区别
  14. fme坐标转换器_FME坐标点提取
  15. 大数据定义、思维方式及架构模式
  16. 数字藏品盲盒系统功能开发H5源码搭建
  17. iOS12 Siri ShortCuts 应用 (二)
  18. 地平线发布AI on Horizon战略,与首汽约车、禾赛科技分别达成战略合作 | 2019上海车展...
  19. 【评测】iPS细胞相关实验服务机构-魔法师的仓库
  20. c++ 家谱管理系统项目文档

热门文章

  1. 支援 Chrome 插件:微软 Chromium 内核 Edge 浏览器可以下载啦!
  2. ajax 405报错,使用ajax请求时发生随机HTTP错误405
  3. 前、后端分离权限控制设计与实现
  4. mysql union 与 union all 语法及用法
  5. vuex 的模块化+命名空间
  6. Vue 计算属性 computed
  7. osgi框架 android,基于OSGi的Android应用模块动态加载框架设计与实现
  8. java string字符操作_Java对String类型字符串的各种操作姿势
  9. java取json对象的值_java的JsonObject对象提取值方法
  10. Java反射————Method根据方法名称字符串调用方法