转载自 http://ifeve.com/java-atomic/

本文首发于并发网,作者:方腾飞

引言

Java从JDK1.5开始提供了java.util.concurrent.atomic包,方便程序员在多线程环境下,无锁的进行原子操作。原子变量的底层使用了处理器提供的原子指令,但是不同的CPU架构可能提供的原子指令不一样,也有可能需要某种形式的内部锁,所以该方法不能绝对保证线程不被阻塞。

Atomic包介绍

在Atomic包里一共有12个类,四种原子更新方式,分别是原子更新基本类型,原子更新数组,原子更新引用和原子更新字段。Atomic包里的类基本都是使用Unsafe实现的包装类。

原子更新基本类型类

用于通过原子的方式更新基本类型,Atomic包提供了以下三个类:

  • AtomicBoolean:原子更新布尔类型。
  • AtomicInteger:原子更新整型。
  • AtomicLong:原子更新长整型。

AtomicInteger的常用方法如下:

  • int addAndGet(int delta) :以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果
  • boolean compareAndSet(int expect, int update) :如果输入的数值等于预期值,则以原子方式将该值设置为输入的值。
  • int getAndIncrement():以原子方式将当前值加1,注意:这里返回的是自增前的值。
  • void lazySet(int newValue):最终会设置成newValue,使用lazySet设置值后,可能导致其他线程在之后的一小段时间内还是可以读到旧的值。关于该方法的更多信息可以参考并发网翻译的一篇文章《AtomicLong.lazySet是如何工作的?》
  • int getAndSet(int newValue):以原子方式设置为newValue的值,并返回旧值。

AtomicInteger例子代码如下:

01 import java.util.concurrent.atomic.AtomicInteger;
02  
03 public class AtomicIntegerTest {
04  
05     static AtomicInteger ai = new AtomicInteger(1);
06  
07     public static void main(String[] args) {
08         System.out.println(ai.getAndIncrement());
09         System.out.println(ai.get());
10     }
11  
12 }

输出

1
2

餐后甜点

Atomic包提供了三种基本类型的原子更新,但是Java的基本类型里还有char,float和double等。那么问题来了,如何原子的更新其他的基本类型呢?Atomic包里的类基本都是使用Unsafe实现的,让我们一起看下Unsafe的源码,发现Unsafe只提供了三种CAS方法,compareAndSwapObject,compareAndSwapInt和compareAndSwapLong,再看AtomicBoolean源码,发现其是先把Boolean转换成整型,再使用compareAndSwapInt进行CAS,所以原子更新double也可以用类似的思路来实现。

原子更新数组类

通过原子的方式更新数组里的某个元素,Atomic包提供了以下三个类:

  • AtomicIntegerArray:原子更新整型数组里的元素。
  • AtomicLongArray:原子更新长整型数组里的元素。
  • AtomicReferenceArray:原子更新引用类型数组里的元素。

AtomicIntegerArray类主要是提供原子的方式更新数组里的整型,其常用方法如下

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

实例代码如下:

01 public class AtomicIntegerArrayTest {
02  
03     static int[] value = new int[] { 12 };
04  
05     static AtomicIntegerArray ai = new AtomicIntegerArray(value);
06  
07     public static void main(String[] args) {
08         ai.getAndSet(03);
09         System.out.println(ai.get(0));
10                 System.out.println(value[0]);
11     }
12  
13 }

输出

3
1

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

原子更新引用类型

原子更新基本类型的AtomicInteger,只能更新一个变量,如果要原子的更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic包提供了以下三个类:

  • AtomicReference:原子更新引用类型。
  • AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
  • AtomicMarkableReference:原子更新带有标记位的引用类型。可以原子的更新一个布尔类型的标记位和引用类型。构造方法是AtomicMarkableReference(V initialRef, boolean initialMark)

AtomicReference的使用例子代码如下:

01 public class AtomicReferenceTest {
02  
03     public static AtomicReference<user> atomicUserRef = new AtomicReference</user><user>();
04  
05     public static void main(String[] args) {
06         User user = new User("conan"15);
07         atomicUserRef.set(user);
08         User updateUser = new User("Shinichi"17);
09         atomicUserRef.compareAndSet(user, updateUser);
10         System.out.println(atomicUserRef.get().getName());
11         System.out.println(atomicUserRef.get().getOld());
12     }
13  
14     static class User {
15         private String name;
16         private int old;
17  
18         public User(String name, int old) {
19             this.name = name;
20             this.old = old;
21         }
22  
23         public String getName() {
24             return name;
25         }
26  
27         public int getOld() {
28             return old;
29         }
30     }
31 }

输出

Shinichi
17

原子更新字段类

如果我们只需要某个类里的某个字段,那么就需要使用原子更新字段类,Atomic包提供了以下三个类:

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

原子更新字段类都是抽象类,每次使用都时候必须使用静态方法newUpdater创建一个更新器。原子更新类的字段的必须使用public volatile修饰符。AtomicIntegerFieldUpdater的例子代码如下:

01 public class AtomicIntegerFieldUpdaterTest {
02  
03     private static AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater
04             .newUpdater(User.class"old");
05  
06     public static void main(String[] args) {
07         User conan = new User("conan"10);
08         System.out.println(a.getAndIncrement(conan));
09         System.out.println(a.get(conan));
10     }
11  
12     public static class User {
13         private String name;
14         public volatile int old;
15  
16         public User(String name, int old) {
17             this.name = name;
18             this.old = old;
19         }
20  
21         public String getName() {
22             return name;
23         }
24  
25         public int getOld() {
26             return old;
27         }
28     }
29 }

输出

10
11

参考资料

Java中的Atomic包使用指南相关推荐

  1. Java中的Atomic包

    Atomic包的作用 方便程序员在多线程环境下,无锁的进行原子操作 Atomic包核心 Atomic包里的类基本都是使用Unsafe实现的包装类,核心操作是CAS原子操作: 关于CAS compare ...

  2. JDK中的Atomic包中的类及使用

    引言 Java从JDK1.5开始提供了java.util.concurrent.atomic包,方便程序员在多线程环境下,无锁的进行原子操作.原子变量的底层使用了处理器提供的原子指令,但是不同的CPU ...

  3. Java进阶——Java中的Atomic原子特性

    引言 这篇文章会从基本概念中入手,首先,从volatile关键字引出原子性的概念和Atomic包,然后,介绍Atomic在使用中的用到的CAS技术和遇到的ABA问题,最后,介绍Atomic的成员和例子 ...

  4. java中的Atomic类

    文章目录 问题背景 Lock 使用Atomic java中的Atomic类 问题背景 在多线程环境中,我们最常遇到的问题就是变量的值进行同步.因为变量需要在多线程中进行共享,所以我们必须需要采用一定的 ...

  5. java中常用的包、类、以及包中常用的类、方法、属性----sql和text\swing

    java中常用的包.类.以及包中常用的类.方法.属性 常用的包 java.io.*; java.util.*; java.lang.*; java.sql.*; java.text.*; java.a ...

  6. 打包部署后无法读取jar包里的文件(实测可行,Java中读取jar包中的文件)

    打包部署后无法读取jar包里的文件 Java中读取jar包中的文件 linux中无法读取jar包中的内容(windows可以的!),如何解决 一.背景 项目中免不了需要读取文件,如果文件用绝对路径读取 ...

  7. JAVA 中的代码生成包 CGLIB (Code Generation Library)

    JAVA 中的代码生成包 CGLIB (Code Generation Library) CGLIB 是一个功能强大,高性能的代码生成包.它为没有实现接口的类提供代理,为 JDK 的动态代理提供了很好 ...

  8. 【JDK源码】java.util.concurrent.atomic包常用类详解

    java.util.concurrent.atomic原子操作类包里面提供了一组原子变量类.其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方 ...

  9. atomic java_在Java中添加@atomic操作

    atomic java 总览 原子操作如何在Java中工作,OpenJDK / Hotspot中是否存在可以转换为原子的当前替代方法. 反馈 在我以前的文章中, 对可变字段进行原子操作. 有几次指出, ...

最新文章

  1. 让别人不知道你的网站使用PHP
  2. Vivado开发过程中生成的主要文件
  3. 4.8地铁查询开发进度
  4. group by 与 order by
  5. Python第五天 文件访问 for循环访问文件 while循环访问文件 字符串的startswith函数和split函数 linecache模块...
  6. C#使用SmtpClient发送邮件解决授权码配置问题
  7. 【FIRST USE】第一次用git把代码上传到github
  8. 2. PHP 自动转义函数
  9. scholarscope不显示影响因子_一劳永逸:这两个查看影响因子的插件你安装了吗?...
  10. 杭州女程序员自述:疫情之下被迫离职,仲裁说理被公司索赔百万
  11. 使android桌面图标变大
  12. Matter理论介绍-通用-1-03:桥接器-数据结构
  13. win10去掉快捷方式小箭头_Win10系统去除桌面快捷方式小箭头图标的三种方法
  14. <XII>无人值守安装脚本
  15. linux中mysql客户端命令行连接不了 docker 创建的mysql
  16. 阿里云服务 - Java实现短信验证码发送 --(完整教程)
  17. Laplace变换的引入
  18. 由路由器AP隔离引起的WEB服务不能访问的问题
  19. Nimbuzz使用心得
  20. API——常见的互联网实名认证方式

热门文章

  1. 28.特性trait.rs
  2. 漫游Kafka设计篇之数据持久化
  3. cocos2d-x初探学习笔记(21)--精灵类
  4. STL 之count,count_if,max,max_element,min,min_element和random_shuffle
  5. 如何定位Release程序崩溃原因
  6. Python 让所有奇数都在偶数前面,而且奇数升序排列,偶数降序排序
  7. 深入理解Linux异步I/O框架 io_uring
  8. Kafka分区分配计算(分区器Partitions)
  9. 又是金三银四,都是Java程序员,你为什么面试总拿不到高薪?
  10. 【免费活动】字节跳动背后的音视频技术揭秘