彻底理解Java并发:Java并发原子类
本篇内容包括:原子类概述、原子类分类(Atomic 基本类型原子类、Array 数组类型原子类、Atomic\Reference 引用类型原子类、Atomic\FieldUpdater 原子更新属性、Adder 加法器、Accumulator 积累器)、原子类 Demo 等内容!
一、原子类概述
我们把一个或者多个操作在 CPU 执行的过程中不能被中断的特性称之为原子性。
在 Jdk1.5 开始 Java 开始引进提供了 java.util.concurrent.atomic
包,到 Jdk8 时,atomic 包共提供了 16 个原子类,分为 6 种类型,分别是:①、基本类型原子类;②、数组类型原子类;③、引用类型原子类;④、原子更新属性;⑤、Adder 加法器;⑥、积累器。
当多线程更新变量的值时,可能得不到预期的值,当然增加 syncronized 关键字可以解决线程并发的问题。但原子类提供了一种用法简单,性能高效,线程安全的更新变量的方式。原子类基本都是使用 Unsafe 实现的包装类,主要用到了 Unsafe 的系统层面的 CAS 实现。
原子类相较于 synchronized 关键字和 lock,有着以下的优点:
简单:操作简单,底层实现简单
高效:占用资源少,操作速度快
安全:在高并发和多线程环境下要保证数据的正确性
对于是需要简单的递增或者递减的需求场景,使用 synchronized 关键字和 lock 固然可以实现,但代码写的会略显冗余,且性能会有影响,此时用原子类更加方便。
二、原子类分类
atomic 包共提供了 16 个原子类,分为 6 种类型:
1、Atomic(基本类型原子类)
Atomic 基本类型原子类,包括三种:AtomicInteger、AtomicLong 和 AtomicBoolean。
AtomicInteger、 AtomicLong、 AtomicBoolean 提供对 int、long、boolean 的原子性操作,这 3 个类提供的方法几乎一模一样。以 AtomicInteger 为例,它包含如下常用的方法:getAndAdd()
返回旧值;addAndGet()
返回新值;getAndIncrement()
加1;incrementAndGet()
、compareAndSet()
原子替换值等。
对于其他基本类型的变量,如 char、float、double,可以先转换为整型,然后再进行原子操作。例如,AtomicBoolean 就是先把 Boolean 转换成整型,再使用 compareAndSwaplnt 进行 CAS 操作。
2、Array(数组类型原子类)
Array 数组类型原子类,包括三种:AtomicIntegerArray、AtomicLongArray 和 AtomicReferenceArray。
AtomicIntegerArray、AtomicLongArray 和 AtomicReferenceArray 提供对 int、long、boolean 的数组元素的原子性操作。原子替换数组中的元素:求i个元素的偏移量,提高位移运算,提高性能。
3、Atomic\Reference(引用类型原子类)
Atomic\Reference 引用类型原子类,包括三种:AtomicReference、AtomicStampedReference 和 AtomicMarkableReference。
AtomicReference 提供了对 对象类型的原子性操作。
AtomicStampedReference 和 AtomicMarkableReference 以版本戳的方式解决原子类型的 ABA 问题,其中 AtomicStampedReference 是原子更新带有标记位(整数)的引用类型;AtomicMarkableReference 是原子更新带有标记位(布尔)的引用类型。
4、Atomic\FieldUpdater(原子更新属性)
Atomic\FieldUpdater 原子更新属性,包括三种:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater。
Atomic\FieldUpdater 原子更新属性,提供对指定对象的指定字段进行原子性操作
如果一个类是自己编写的,则可以在编写的时候把成员变量定义为 Atomic 类型。但如果是一个已经有的类,在不能更改其源代码的情况下,要想实现对其成员变量的原子操作,就需要使用 AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater 三个类,将要使用的传给这个类,让其去做原子更新操作。
5、Adder(加法器)
Adder 加法器,包括两种:LongAdder 和 DoubleAdder。
Atomic 基本类型,可以保证多线程下的线程安全。但是,在并发量很大的场景下,Atomic 基本类型原子类(AtomicInteger 和 AtomicLong)有很大的性能问题。LongAdder 和 DoubleAdder 就是 Atomic 基本类型原子类的升级类型,专门用于数据统计,性能更高!
6、Accumulator(积累器)
Accumulator 积累器,包括两种:LongAccumulator 和 DoubleAccumulator。
Accumulator 和 Adder 非常相似,实际上 Accumulator 就是一个更通用版本的 Adder,比如 LongAccumulator 是 LongAdder 的功能增强版,因为 LongAdder 的 API 只有对数值的加减,而 LongAccumulator 提供了自定义的函数操作。
三、原子类Demo
这里以基本类型原子类中的 AtomicInteger 类为例,介绍通用的 API 接口和使用方法。
首先是几个常用的API:
// 以原子方式将给定值与当前值相加,可用于线程中的计数使用,(返回更新的值)。
int addAndGet(int delta)// 以原子方式将给定值与当前值相加,可用于线程中的计数使用,(返回以前的值)
int getAndAdd(int delta)// 以原子方式将当前值加 1(返回更新的值)
int incrementAndGet()// 以原子方式将当前值加 1(返回以前的值)
int getAndIncrement() // 以原子方式设置为给定值(返回旧值)
int getAndSet(int newValue)// 以原子方式将当前值减 1(返回更新的值)
int decrementAndGet() :// 以原子方式将当前值减 1(返回以前的值)
int getAndDecrement()// 获取当前值
get()
这里定义一个临界变量 val,起 10 个异步线程,每个线程都是对这个临界变量进行 10000 次自增操作,如下:
public class AtomicWrongDemo {private int val = 0;public static void main(String[] args) {// 初始化实例AtomicWrongDemo atomicWrongDemo = new AtomicWrongDemo();for (int i = 0; i < 10; ++i) {new Thread(atomicWrongDemo::increase).start();}// 让主线程休眠3秒,保证前面起的10个异步线程都执行完毕try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(atomicWrongDemo.getVal());}private void increase() {for (int i = 0; i < 20000; ++i) {++this.val;}Thread t = Thread.currentThread();System.out.println("线程:" + t.getName() + "已经执行完毕,当前 val 结果为" + this.val);}private int getVal() {return this.val;}
}
运行结果会我们期望的 100000 少很多(操作数越大,距期望值相差越多),比如我这里结果为 39757,出现比 100000 少很多的结果,是因为自增操作 ++i 不是原子操作,出现了竞争,需要对临界变量做同步处理。
使用 synchronized 关键字和 lock 固然可以实现,但这里只是对临界变量 val++ 时做同步处理,有种高射炮打蚊子的感觉,且加锁后势必会对性能有所印象,这种场景正是我们使用 Atomic 类的场景,如下:
public class AtomicDemo {private AtomicInteger val = new AtomicInteger();public static void main(String[] args) {// 初始化实例AtomicDemo atomicDemo = new AtomicDemo();for (int i = 0; i < 10; ++i) {new Thread(atomicDemo::increase).start();}// 让主线程休眠3秒,保证前面起的10个异步线程都执行完毕try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(atomicDemo.getVal().toString());}private void increase() {for (int i = 0; i < 10000; ++i) {this.val.incrementAndGet();}}private AtomicInteger getVal() {return this.val;}
}
这里我们使用了 AtomicInterger 类的 increamentAndGet 方法,以原子方式将当前值加 1(返回更新的值),结果自然是每次运行都打印 100000,可以看到代码写起来很简洁,很轻量级。
彻底理解Java并发:Java并发原子类相关推荐
- Java中常用的原子类
文章目录 一.什么是原子类 二.原子类的底层实现 三.常用的原子类 3.1.AtomicInteger与AtomicLong 3.2.LongAdder 四.原子类的性能测试 4.1.测试程序 4.2 ...
- Java多线程系列--“JUC原子类”01之 框架
2019独角兽企业重金招聘Python工程师标准>>> Java多线程系列--"JUC原子类"01之 框架 根据修改的数据类型,可以将JUC包中的原子操作类可以分 ...
- Java多线程系列--“JUC原子类”03之 AtomicLongArray原子类
概要 AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray这3个数组类型的原子类的原理和用法相似.本章以AtomicLongArray对数 ...
- Java并发编程实战~原子类
对于简单的原子性问题,还有一种无锁方案,先看看如何利用原子类解决累加器问题. public class Test {AtomicLong count = new AtomicLong(0);publi ...
- Java高并发编程:原子类
1. 并发编程概念 原子性 一个操作不能被再拆分了:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行.一个很经典的例子就是银行账户转账问题. 增量操作符++,不是原 ...
- Java并发编程—Atomic原子类
目录 Atomic 1. AtomicInteger a. 多线程并发访问问题 b. 用 AtomicInteger 类解决 2. AtomicIntegerArray a. 多线程并发访问问题 b. ...
- Java多线程系列---“JUC原子类”02之 框架
转自:http://www.cnblogs.com/skywang12345/p/3514589.html 根据修改的数据类型,可以将JUC包中的原子操作类可以分为4类. 1. 基本类型: Atomi ...
- java并发:原子类之AtomicLong
原子类之AtomicLong java线程中的操作,需要满足原子性.可见性等原则,比如i++这样的操作不具备原子性, A线程读取了i,另一个线程执行i++,A线程再执行i++就会引发线程安全问题 推荐 ...
- 【牛客网】-【并发详解】-【并发编程基础】-【原子类】
目录 并发编程基础 原子类 参考书目: 并发编程基础 在操作系统中,并发是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处 ...
- Java多线程进阶面试-Atomic 原子类
1.介绍一下 Atomic 原子类 Atomic 翻译成中文是原子的意思.在化学上,我们知道原子是构成一般物质的最小单位,在化学反应中是不可分割的.在我们这里 Atomic 是指一个操作是不可中断的. ...
最新文章
- WIN10安装scrapy/channels等不成功的解决方式
- esp8266设置sta失败_使用NodeMCU_ESP8266驱动OLED
- 《3D数学基础》实践1 向量类代码分析
- 【转】数据库设计:物理结构设计
- Keras入门必读教程:手把手从安装到解决实际问题
- 超分辨率算法大战!AI in RTC 创新挑战赛——20万巨奖等你来拿!
- day20 java的String
- Happy Programming Contest
- 有NFC功能的手机可以刷小区门禁吗?
- Mysql 5 replication(mysql主从双机策略)
- java pgp加密_GPG(pgp)加解密中文完整教程
- 用DropDownList做的日期
- Gradle之SourceSet
- 进制转化(北理乐学编程题目)
- pop3邮箱怎么设置收发服务器端口,常用邮箱的服务器(Smtp/POP3)地址和端口总结
- 易飞计件工资的设计及应用
- 计算n阶行列式的C语言实现
- 左边是地狱右边也是地狱_我担任地狱首席执行官的时间
- 二零二二,闯北十年,及时行乐,此生尽兴
- 文心一言的魔性作图,我愣住了……