Java中的Atomic包使用指南
转载自 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 [] { 1 , 2 };
|
04
|
05
|
static AtomicIntegerArray ai = new AtomicIntegerArray(value);
|
06
|
07
|
public static void main(String[] args) {
|
08
|
ai.getAndSet( 0 , 3 );
|
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包使用指南相关推荐
- Java中的Atomic包
Atomic包的作用 方便程序员在多线程环境下,无锁的进行原子操作 Atomic包核心 Atomic包里的类基本都是使用Unsafe实现的包装类,核心操作是CAS原子操作: 关于CAS compare ...
- JDK中的Atomic包中的类及使用
引言 Java从JDK1.5开始提供了java.util.concurrent.atomic包,方便程序员在多线程环境下,无锁的进行原子操作.原子变量的底层使用了处理器提供的原子指令,但是不同的CPU ...
- Java进阶——Java中的Atomic原子特性
引言 这篇文章会从基本概念中入手,首先,从volatile关键字引出原子性的概念和Atomic包,然后,介绍Atomic在使用中的用到的CAS技术和遇到的ABA问题,最后,介绍Atomic的成员和例子 ...
- java中的Atomic类
文章目录 问题背景 Lock 使用Atomic java中的Atomic类 问题背景 在多线程环境中,我们最常遇到的问题就是变量的值进行同步.因为变量需要在多线程中进行共享,所以我们必须需要采用一定的 ...
- java中常用的包、类、以及包中常用的类、方法、属性----sql和text\swing
java中常用的包.类.以及包中常用的类.方法.属性 常用的包 java.io.*; java.util.*; java.lang.*; java.sql.*; java.text.*; java.a ...
- 打包部署后无法读取jar包里的文件(实测可行,Java中读取jar包中的文件)
打包部署后无法读取jar包里的文件 Java中读取jar包中的文件 linux中无法读取jar包中的内容(windows可以的!),如何解决 一.背景 项目中免不了需要读取文件,如果文件用绝对路径读取 ...
- JAVA 中的代码生成包 CGLIB (Code Generation Library)
JAVA 中的代码生成包 CGLIB (Code Generation Library) CGLIB 是一个功能强大,高性能的代码生成包.它为没有实现接口的类提供代理,为 JDK 的动态代理提供了很好 ...
- 【JDK源码】java.util.concurrent.atomic包常用类详解
java.util.concurrent.atomic原子操作类包里面提供了一组原子变量类.其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方 ...
- atomic java_在Java中添加@atomic操作
atomic java 总览 原子操作如何在Java中工作,OpenJDK / Hotspot中是否存在可以转换为原子的当前替代方法. 反馈 在我以前的文章中, 对可变字段进行原子操作. 有几次指出, ...
最新文章
- 让别人不知道你的网站使用PHP
- Vivado开发过程中生成的主要文件
- 4.8地铁查询开发进度
- group by 与 order by
- Python第五天 文件访问 for循环访问文件 while循环访问文件 字符串的startswith函数和split函数 linecache模块...
- C#使用SmtpClient发送邮件解决授权码配置问题
- 【FIRST USE】第一次用git把代码上传到github
- 2. PHP 自动转义函数
- scholarscope不显示影响因子_一劳永逸:这两个查看影响因子的插件你安装了吗?...
- 杭州女程序员自述:疫情之下被迫离职,仲裁说理被公司索赔百万
- 使android桌面图标变大
- Matter理论介绍-通用-1-03:桥接器-数据结构
- win10去掉快捷方式小箭头_Win10系统去除桌面快捷方式小箭头图标的三种方法
- <XII>无人值守安装脚本
- linux中mysql客户端命令行连接不了 docker 创建的mysql
- 阿里云服务 - Java实现短信验证码发送 --(完整教程)
- Laplace变换的引入
- 由路由器AP隔离引起的WEB服务不能访问的问题
- Nimbuzz使用心得
- API——常见的互联网实名认证方式
热门文章
- 28.特性trait.rs
- 漫游Kafka设计篇之数据持久化
- cocos2d-x初探学习笔记(21)--精灵类
- STL 之count,count_if,max,max_element,min,min_element和random_shuffle
- 如何定位Release程序崩溃原因
- Python 让所有奇数都在偶数前面,而且奇数升序排列,偶数降序排序
- 深入理解Linux异步I/O框架 io_uring
- Kafka分区分配计算(分区器Partitions)
- 又是金三银四,都是Java程序员,你为什么面试总拿不到高薪?
- 【免费活动】字节跳动背后的音视频技术揭秘