JDK中的Atomic包中的类及使用
引言
Java从JDK1.5开始提供了java.util.concurrent.atomic包,方便程序员在多线程环境下,无锁的进行原子操作。原子变量的底层使用了处理器提供的原子指令,但是不同的CPU架构可能提供的原子指令不一样,也有可能需要某种形式的内部锁,所以该方法不能绝对保证线程不被阻塞。
Atomic包介绍
在JDK1.8 Atomic包里一共有17个类,四种原子更新方式,分别是原子更新基本类型,原子更新数组,原子更新引用和原子更新字段。Atomic包里的类基本都是使用Unsafe实现的包装类。
1. 原子更新基本类型类
用于通过原子的方式更新基本类型,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例子代码如下:
|
1
2
3
4
5
6
7
8
|
import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerTest { static AtomicInteger ai = new AtomicInteger(1); public static void main(String[] args) { System.out.println(ai.getAndIncrement()); System.out.println(ai.get()); }} |
输出
|
1
2
|
12 |
餐后甜点
Atomic包提供了三种基本类型的原子更新,但是Java的基本类型里还有char,float和double等。那么问题来了,如何原子的更新其他的基本类型呢?Atomic包里的类基本都是使用Unsafe实现的,让我们一起看下Unsafe的源码,发现Unsafe只提供了三种CAS方法,compareAndSwapObject,compareAndSwapInt和compareAndSwapLong,再看AtomicBoolean源码,发现其是先把Boolean转换成整型,再使用compareAndSwapInt进行CAS,所以原子更新double也可以用类似的思路来实现。
2. 原子更新数组类
通过原子的方式更新数组里的某个元素,Atomic包提供了以下三个类:
- AtomicIntegerArray:原子更新整型数组里的元素。
- AtomicLongArray:原子更新长整型数组里的元素。
- AtomicReferenceArray:原子更新引用类型数组里的元素。
AtomicIntegerArray类主要是提供原子的方式更新数组里的整型,其常用方法如下
- int addAndGet(int i, int delta):以原子方式将输入值与数组中索引i的元素相加。
- boolean compareAndSet(int i, int expect, int update):如果当前值等于预期值,则以原子方式将数组位置i的元素设置成update值。
实例代码如下:
|
1
2
3
4
5
6
7
8
9
10
|
public class AtomicIntegerArrayTest { static int[] value = new int[] { 1, 2 }; static AtomicIntegerArray ai = new AtomicIntegerArray(value); public static void main(String[] args) { ai.getAndSet(0, 3); System.out.println(ai.get(0)); System.out.println(value[0]); } } |
输出
|
1
2
|
31 |
AtomicIntegerArray类需要注意的是,数组value通过构造方法传递进去,然后AtomicIntegerArray会将当前数组复制一份,所以当AtomicIntegerArray对内部的数组元素进行修改时,不会影响到传入的数组。
3. 原子更新引用类型
原子更新基本类型的AtomicInteger,只能更新一个变量,如果要原子的更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic包提供了以下三个类:
- AtomicReference:原子更新引用类型。
- AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
- AtomicMarkableReference:原子更新带有标记位的引用类型。可以原子的更新一个布尔类型的标记位和引用类型。构造方法是AtomicMarkableReference(V initialRef, boolean initialMark)
AtomicReference的使用例子代码如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public class AtomicReferenceTest { public static AtomicReference<user> atomicUserRef = new AtomicReference</user><user>(); public static void main(String[] args) { User user = new User("conan", 15); atomicUserRef.set(user); User updateUser = new User("Shinichi", 17); atomicUserRef.compareAndSet(user, updateUser); System.out.println(atomicUserRef.get().getName()); System.out.println(atomicUserRef.get().getOld()); } static class User { private String name; private int old; public User(String name, int old) { this.name = name; this.old = old; } public String getName() { return name; } public int getOld() { return old; } }} |
输出
|
1
2
|
Shinichi17 |
4. 原子更新字段类
如果我们只需要某个类里的某个字段,那么就需要使用原子更新字段类,Atomic包提供了以下三个类:
- AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
- AtomicLongFieldUpdater:原子更新长整型字段的更新器。
- AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更数据和数据的版本号,可以解决使用CAS进行原子更新时,可能出现的ABA问题。
原子更新字段类都是抽象类,每次使用都时候必须使用静态方法newUpdater创建一个更新器。原子更新类的字段的必须使用public volatile修饰符。AtomicIntegerFieldUpdater的例子代码如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public class AtomicIntegerFieldUpdaterTest { private static AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater .newUpdater(User.class, "old"); public static void main(String[] args) { User conan = new User("conan", 10); System.out.println(a.getAndIncrement(conan)); System.out.println(a.get(conan)); } public static class User { private String name; public volatile int old; public User(String name, int old) { this.name = name; this.old = old; } public String getName() { return name; } public int getOld() { return old; } }} |
输出
|
1
2
|
1011 |
JDK中的Atomic包中的类及使用的更多相关文章
- 24.Java中atomic包中的原子操作类总结
1. 原子操作类介绍 在并发编程中很容易出现并发安全的问题,有一个很简单的例子就是多线程更新变量i=1,比如多个线程执行i++操作,就有可能获取不到正确的值,而这个问题,最常用的方法是通过Synchr ...
- Java中的Atomic包使用指南
Atomic包介绍 在Atomic包里一共有12个类,四种原子更新方式,分别是原子更新基本类型,原子更新数组,原子更新引用和原子更新字段.Atomic包里的类基本都是使用Unsafe实现的包装类. 原 ...
- Java日期时间API系列13-----Jdk8中java.time包中的新的日期时间API类,时间类转换,Date转LocalDateTime,LocalDateTime转Date等
从前面的系列博客中可以看出Jdk8中java.time包中的新的日期时间API类设计的很好,但Date由于使用仍非常广泛,这就涉及到Date转LocalDateTime,LocalDateTime转D ...
- Java日期时间API系列19-----Jdk8中java.time包中的新的日期时间API类,ZonedDateTime与ZoneId和LocalDateTime的关系,ZonedDateTime格式化和时区转换等。
通过Java日期时间API系列6-----Jdk8中java.time包中的新的日期时间API类中时间范围示意图:可以很清晰的看出ZonedDateTime相当于LocalDateTime+ZoneI ...
- Java日期时间API系列8-----Jdk8中java.time包中的新的日期时间API类的LocalDate源码分析
目录 0.前言 1.TemporalAccessor源码 2.Temporal源码 3.TemporalAdjuster源码 4.ChronoLocalDate源码 5.LocalDate源码 6.总 ...
- Java日期时间API系列11-----Jdk8中java.time包中的新的日期时间API类,使用java8日期时间API重写农历LunarDate
通过Java日期时间API系列7-----Jdk8中java.time包中的新的日期时间API类的优点,java8具有很多优点,现在网上查到的农历转换工具类都是基于jdk7及以前的类写的,下面使用ja ...
- Java日期时间API系列12-----Jdk8中java.time包中的新的日期时间API类,日期格式化,常用日期格式大全
通过Java日期时间API系列10-----Jdk8中java.time包中的新的日期时间API类的DateTimeFormatter, 可以看出java8的DateTimeFormatter完美解决 ...
- Java中的Atomic包
Atomic包的作用 方便程序员在多线程环境下,无锁的进行原子操作 Atomic包核心 Atomic包里的类基本都是使用Unsafe实现的包装类,核心操作是CAS原子操作: 关于CAS compare ...
- 在swt中获取jar包中的文件 uri is not hierarchical
uri is not hierarchical 学习了:http://blog.csdn.net/zdsdiablo/article/details/1519719 在swt中获取jar包中的文件: ...
随机推荐
- 图解MySQL索引--B-Tree(B+Tree)
看了很多关于索引的博客,讲的大同小异.但是始终没有让我明白关于索引的一些概念,如B-Tree索引,Hash索引,唯一索引....或许有很多人和我一样,没搞清楚概念就开始研究B-Tree,B+Tree等 ...
- 你不知道的JavaScript--Item21 漂移的this
而在 JavaScript 中,this 是动态绑定,或称为运行期绑定的,这就导致 JavaScript 中的 this 关键字有能力具备多重含义,带来灵活性的同时,也为初学者带来不少困惑.本文仅就这 ...
- 10. 搭配redis做文章缓存
redis是一个使用较多的内存键值数据库,这儿的键是字符串类型的标识符,而值可以是字符串.散列.列表.集合和有序集合,也正是因为redis提供了较丰富的值的类型,能够满足不同的使用要求,而且redis ...
- python教你用微信每天给女朋友说晚安
但凡一件事,稍微有些重复.我就考虑怎么样用程序来实现它. 这里给各位程序员朋友分享如何每天给朋友定时微信发送"晚安",故事,新闻,等等··· ···最好运行在服务器上,这样后台挂起 ...
- Python3 venv 创建虚拟环境
简介 Python3.3以上的版本通过venv模块原生支持虚拟环境,可以代替Python之前的virtualenv. 该venv模块提供了创建轻量级"虚拟环境",提供与系统Pyth ...
- JSON数据格式转换
json格式 (JavaScipt Object Notation):是一种数据交换格式!json语法规则:01.对象表现形式 key:value 键值对02.如果有多个数据,之间使用逗号隔开 k1: ...
- Java的参数传递是「值传递」还是「引用传递」?
关于Java传参时是引用传递还是值传递,一直是一个讨论比较多的话题. 有人说Java中只有值传递,也有人说值传递和引用传递都是存在的,比较容易让人产生疑问. 关于值传递和引用传递其实需要分情况看待. ...
- java某些基础知识点整理
1. \n换行 \r回车 \"双引号 \\反斜杠 2.Java语言提供了八种基本类型.六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型. byte: byte 数据类型是 ...
- ELK---日志分析系统
ELK就是一套完整的日志分析系统 ELK=Logstash+Elasticsearch+Kibana 统一官网https://www.elastic.co/products ELK模块说明 Logst ...
- MYSQL———正则表达式查询!
在使用select查询的过程中,有时会用到正则表达式对结果进行查询,将学习到的内容进行总结! 一 语法结构如下: 二 常用匹配方式进行示例说明 首先创建表student,表的结构如下: 1·^:查询s ...