J.U.C 系列之Atomic原子类
一 什么是原子类?
所谓原子类必然是具有原子性的类,原子性操作--原子操作,百度百科中给的定义如下
"原子操作(atomic operation)是不需要synchronized",这是Java多线程编程的老生常谈了。所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切[1] 换到另一个线程)。
顾名思义,原子类就是一个一旦被执行就不能中断的类。
二 为什么需要原子类?
在看为什么需要原子类之前,我们看看普通Number类,在处理问题时可能存在的问题,这里我们通过Integer来演示
public class Main { private static Integer sum = 0; public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(new Task()).start();
} Thread.yield();
System.out.println(sum); } static class Task implements Runnable {
@Override
public void run() {
for(int i = 0;i<100;i++){
sum++;
}
}
}
}
这段代码意思是开20个线程,每个线程对sum自加100次,理论上应该最后输出2000;但是事实上每次都是小于2000;
这是听说过volatile关键字的小伙伴可能会说,使用volatile来修饰sum,好,我们继续试验
public class Main { private static volatile Integer sum = 0; public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(new Task()).start();
} Thread.yield();
System.out.println(sum); } static class Task implements Runnable {
@Override
public void run() {
for(int i = 0;i<100;i++){
sum++;
}
}
}
}
这是试验五次的输出
1766,1616,1859,1980,1800
还是都是小于2000,这是怎么回事,这里先提一下,volatile只能保证单个操作的原子性,而sum++,包括三个操作
sum = getSum() //读取sum temp = sum +1; //sum+1,赋给临时变量 sum = setSum(temp) //将sum写回
因此,即使是volatile也无法保证sum++的原子性,volatile只能保证单个操作的原子性,而++操作是复合操作,volatile变量会在后续章节详细讨论;
那么,在Atomic未出现之前,是如何处理i++在多线程环境下的线程安全问题,主要是通过Synchronize加锁来处理,处理过程复杂,性能低
JDK5.0之后出现的Java.util.concurrent.Atomic包中为我们提供了13中原子类,来保证单个原子变量复合操作的原子性。下面我们通过AtomicInteger的使用来认识一下原子类
三 原子类示例详解
AtomicInteger 字段
// setup to use Unsafe.compareAndSwapInt for updates
//这里, unsafe是java提供的获得对对象内存地址访问的类,注释已经清楚的写出了,它的作用就是在更新操作时提供“比较并替换”的作用。实际上就是AtomicInteger中的一个工具。
private static final Unsafe unsafe = Unsafe.getUnsafe();
//valueOffset是用来记录value本身在内存的便宜地址的,这个记录,也主要是为了在更新操作在内存中找到value的位置,方便比较。
private static final long valueOffset;
//value是用来存储整数的时间变量,这里被声明为volatile,就是为了保证在更新操作时,当前线程可以拿到value最新的值(并发环境下,value可能已经被其他线程更新了)。
private volatile int value;
AtomicInteger 构造方法
/**
* Creates a new AtomicInteger with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicInteger(int initialValue) {
value = initialValue;
} /**
* Creates a new AtomicInteger with initial value {@code 0}.
*/
public AtomicInteger() {
}
AtomicInteger 并发安全实现
那么AtomicInteger是如何实现多线程的自增操作的线程安全的呢?核心思想就是CAS自旋;CAS:Compare And Swap 比较并交换。自旋:通过循环知道预期值和内存之相同,进行CAS操作,AtomicInteger的自增如下所示
public final int incrementAndGet() {
for (;;) {
//这里可以拿到value的最新内存值
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
} public final boolean compareAndSet(int expect, int update) {
//使用unsafe的native方法,实现高效的硬件级别CAS
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
AtomicInteger 其他常用方法
/**
* 返回旧值,然后自增1
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
} /**
* 返回旧值,然后自减1
*
* @return the previous value
*/
public final int getAndDecrement() {
return unsafe.getAndAddInt(this, valueOffset, -1);
} /**
* 返回旧值,然后 旧值+delta
*
* @param delta the value to add
* @return the previous value
*/
public final int getAndAdd(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta);
} /**
* 先进行自增,返回自增后的值
*
* @return the updated value
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
} /**
* 先进行自减,然后返回自减后的值
*
* @return the updated value
*/
public final int decrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
} /**
*先在原值上加delta,再返回加之后的值
*
* @param delta the value to add
* @return the updated value
*/
public final int addAndGet(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}
注:其他Atomic类类似,不一一介绍;
四 Atomic 存在的问题
》 长时间自旋,导致CPU和资源的占用
》 只能保证单个原子变量的多线程安全操作,当然可以将多个变量封装成一个类,通过原子引用类型实现
》 ABA问题;使用AtomicStampedReference 原子更新带有版本号的引用类型解决
J.U.C 系列之Atomic原子类的更多相关文章
- JUC 中的 Atomic 原子类总结
1 Atomic 原子类介绍 Atomic 翻译成中文是原子的意思.在化学上,我们知道原子是构成一般物质的最小单位,在化学反应中是不可分割的.在我们这里 Atomic 是指一个操作是不可中断的.即使是 ...
- Atomic原子类
Atomic原子类 Atomic原子类位于并发包java.util.concurrent下的java.util.concurrent.Atomic中. 1. 原子更新基本类型类 使用原子方式更新基本数 ...
- Java CAS同步机制 原理详解(为什么并发环境下的COUNT自增操作不安全): Atomic原子类底层用的不是传统意义的锁机制,而是无锁化的CAS机制,通过CAS机制保证多线程修改一个数值的安全性。
精彩理解: https://www.jianshu.com/p/21be831e851e ; https://blog.csdn.net/heyutao007/article/details/19 ...
- Juc中Atomic原子类总结
1 Atomic原子类介绍 2 基本类型原子类 3 数组类型原子类 4 引用类型原子类 5 对象的属性修改类型原子类
- (转)Java atomic原子类的使用方法和原理(一)
在讲atomic原子类之前先看一个小例子: public class UseAtomic { public static void main(String[] args) { AtomicIntege ...
- Java线程--Atomic原子类使用
原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/11871241.html Java线程--Atomic原子类使用 package concurr ...
- 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)
前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...
- [Java多线程]-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)
前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...
- JUC学习笔记--Atomic原子类
J.U.C 框架学习顺序 http://blog.csdn.net/chen7253886/article/details/52769111 Atomic 原子操作类包 Atomic包 主要是在多线程 ...
随机推荐
- staticmethod classmethod property方法
@staticmethod 静态方法 函数修饰符,用来修饰一个函数,类似于装饰器 class Dog(object): def __init__(self,name): self.name = nam ...
- 关于ubuntu安装软件的问题:apt-get和dpkg区别?
两者的区别是dpkg绕过apt包管理数据库对软件包进行操作,所以你用dpkg安装过的软件包用apt可以再安装一遍,系统不知道之前安装过了,将会覆盖之前dpkg的安装.1.dpkg是用来安装.deb文件 ...
- Wampserver由橙变绿的解决过程
因为C盘的内存问题,就重装了win7系统,那么就面临着很对软件要重新进行安装,安装wampserver时,再次遇到了服务器的图标一直是橙色的而不变绿色,安装包地址: http://download.c ...
- C语言中头文件怎么写?(本文来源网络,由黑乌鸦进一步完善)
c语言头文件怎么写?我一直有这样的疑问,但是也一直没去问问到底咋回事:所以今天一定要把它弄明白! 其实学会写头文件之后可以为我们省去不少事情,可以避免书写大量的重复代码.有利于整理思路.使代码脉络 ...
- 单调队列 poj2823,fzu1894
题目链接:http://poj.org/problem?id=2823 用RMQ超时了,我想应该是不会的,看discuss说,之前RMQ过了. 维护两个单调队列. 单调递减的队列,每插入一个时: 超过 ...
- BZOJ 3235: [Ahoi2013]好方的蛇
BZOJ 3235: [Ahoi2013]好方的蛇 标签(空格分隔): OI-BZOJ OI-DP OI-容斥原理 Time Limit: 10 Sec Memory Limit: 64 MB Des ...
- P1909 买铅笔
题目描述 P老师需要去商店买n支铅笔作为小朋友们参加NOIP的礼物.她发现商店一共有 33种包装的铅笔,不同包装内的铅笔数量有可能不同,价格也有可能不同.为了公平起 见,P老师决定只买同一种包装的铅笔 ...
- SpringBoot学习14:springboot异常处理方式4(使用SimpleMappingExceptionResolver处理异常)
修改异常处理方法3中的全局异常处理Controller即可 package bjsxt.exception; import org.springframework.context.annotation ...
- iOS 实时监测网络状态(通过Reachability)
在AppDelegate.m中 @property (nonatomic, strong) Reachability *reach; - (BOOL)application:(UIApplicatio ...
- 【杂题总汇】UVa-1336 Fixing the Great Wall
[UVA-1336]Fixing the Great Wall 一开始把题看错了……直接用的整数存储答案:之后用double存最后输出答案的时候取整就AC了