Java Automic包下的AtomicInteger
感谢这两位博主的文章,文章源于:
https://www.cnblogs.com/chenpi/p/5375805.html
https://blog.csdn.net/fanrenxiang/article/details/80623884
版本:dk1.5后提供了,java.util.concurrent.atomic包
作用:方便程序员在多线程环境下,无锁的进行原子性操作
底层:原子变量的底层使用了处理器提供的原子指令,但是不同的CPU架构可能提供的原子指令不一样,也有可能需要某种形式的内部锁,所以该方法不能绝对保证线程不被阻塞
内部核心:Atomic包里内部实现不是简单的使用synchronized,而是一个更为高效的方式CAS (compare and swap) + volatile和native方法(同步的工作更多的交给了硬件),从而避免了synchronized的高开销,执行效率大为提升
关于CAS:
compare and swap,比较和替换技术,将预期值与当前变量的值比较(compare),如果相等则使用新值替换(swap)当前变量,否则不作操作;
现代CPU已广泛支持CAS指令,如果不支持,那么JVM将使用自旋锁,与互斥锁一样,两者都需先获取锁才能访问共享资源,但互斥锁会导致线程进入睡眠,而自旋锁会一直循环等待直到获取锁;
另外,有一点需要注意的是CAS操作中的ABA问题,即将预期值与当前变量的值比较的时候,即使相等也不能保证变量没有被修改过,因为变量可能由A变成B再变回A,解决该问题,可以给变量增加一个版本号,每次修改变量时版本号自增,比较的时候,同时比较变量的值和版本号即可;
Atomic包中原子方式基本更新类型:
1.AtomicBoolean:原子更新布尔类型。
2.AtomicInteger:原子更新整型。
3.AtomicLong:原子更新长整型。
以AtomicInteger为例,源码:
package concurrency;
import java.util.concurrent.atomic.AtomicInteger; public class AtomicIntegerTest { static AtomicInteger ai = new AtomicInteger(1); public static void main(String[] args) {
//相当于i++,返回的是旧值,看方法名就知道,先获取再自增
System.out.println(ai.getAndIncrement());
System.out.println(ai.get());
//先自增,再获取
System.out.println(ai.incrementAndGet());
System.out.println(ai.get());
//增加一个指定值,先add,再get
System.out.println(ai.addAndGet(5));
System.out.println(ai.get());
//增加一个指定值,先get,再set
System.out.println(ai.getAndSet(5));
System.out.println(ai.get());
} }
为什么需要AtomicInteger原子操作类?
对于Java中的运算操作,例如自增或自减,若没有进行额外的同步操作,在多线程环境下就是线程不安全的。num++解析为num=num+1,明显,这个操作不具备原子性,多线程并发共享这个变量时必然会出现问题。测试代码如下:
public class AtomicIntegerTest { private static final int THREADS_CONUT = 20;
public static int count = 0; public static void increase() {
count++;
} public static void main(String[] args) {
Thread[] threads = new Thread[THREADS_CONUT];
for (int i = 0; i < THREADS_CONUT; i++) {
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
increase();
}
}
});
threads[i].start();
} while (Thread.activeCount() > 1) {
Thread.yield();
}
System.out.println(count);
}
}
这里运行了20个线程,每个线程对count变量进行1000此自增操作,如果上面这段代码能够正常并发的话,最后的结果应该是20000才对,但实际结果却发现每次运行的结果都不相同,都是一个小于20000的数字。
再用AtomicInteger:
import java.util.concurrent.atomic.AtomicInteger; public class AtomicIntegerTest { private static final int THREADS_CONUT = 20;
public static AtomicInteger count = new AtomicInteger(0); public static void increase() {
count.incrementAndGet();
} public static void main(String[] args) {
Thread[] threads = new Thread[THREADS_CONUT];
for (int i = 0; i < THREADS_CONUT; i++) {
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
increase();
}
}
});
threads[i].start();
} while (Thread.activeCount() > 1) {
Thread.yield();
}
System.out.println(count);
}
}
结果每次都输出20000,程序输出了正确的结果,这都归功于AtomicInteger.incrementAndGet()方法的原子性。
注意:Atomic包提供了三种基本类型的原子更新,剩余的Java的基本类型还有char,float和double等,其更新方式可以参考AtomicBoolean的思路来现,AtomicBoolean是把boolean转成整型再调用compareAndSwapInt进行CAS来实现的,类似的short和byte也可以转成整形,float和double可以利用Float.floatToIntBits,Double.doubleToLongBits转成整形和长整形进行相应处理;
在中低程度的竞争下,原子类提供更高的伸缩性;在高强度的竞争下,锁能更好的帮助我们避免竞争。(来自《并发编程实战》)
所以,我们要视情况而定,若资源竞争规模不大,控制粒度较小,使用原子类比使用锁更好,能提高效率与性能
Java Automic包下的AtomicInteger的更多相关文章
- List,Set,Map在java.util包下都是接口 List有两个实现类:ArrayList和LinkedList Set有两个实现类:HashSet和LinkedHashSet AbstractSet实现了Set
List,Set,Map在java.util包下都是接口 List有两个实现类:ArrayList和LinkedListSet有两个实现类:HashSet和LinkedHashSetAbstractS ...
- java.io 包下的类有哪些 + 面试题
java.io 包下的类有哪些 + 面试题 IO 介绍 IO 是 Input/Output 的缩写,它是基于流模型实现的,比如操作文件时使用输入流和输出流来写入和读取文件等. IO 分类 传统的 IO ...
- [Java多线程]-J.U.C.atomic包下的AtomicInteger,AtomicLong等类的源码解析
Atomic原子类:为基本类型的封装类Boolean,Integer,Long,对象引用等提供原子操作. 一.Atomic包下的所有类如下表: 类摘要 AtomicBoolean 可以用原子方式更新的 ...
- 多线程爬坑之路-J.U.C.atomic包下的AtomicInteger,AtomicLong等类的源码解析
Atomic原子类:为基本类型的封装类Boolean,Integer,Long,对象引用等提供原子操作. 一.Atomic包下的所有类如下表: 类摘要 AtomicBoolean 可以用原子方式更新的 ...
- Java 扫描包下所有类(包括jar包)
package com.MyUtils.file; [java] view plain copy import java.io.File; import java.io.FileFilter; imp ...
- java.io包下适配和装饰模式的使用
如java.io.LineNumberInputStream(deprecated),是装饰模式(decorate)的实现: 如java.io.OutputStreamWriter,是适配器模式(ad ...
- mongodb在java驱动包下的操作(转)
推荐几章很有用的文章 java操作参考文档 http://www.cnblogs.com/hoojo/archive/2011/06/02/2068665.html http://blog.csdn. ...
- JAVA concurrent包下Semaphore、CountDownLatch等用法
CountDownLatch 跟join的区别 CountDownLatch用处跟join很像,但是CountDownLatch更加灵活,如果子线程有多个阶段a.b.c; 那么我们可以实现在a阶段完成 ...
- 【第二周】关于java.util包下的Random类
1.功能:此类的实例用于生成伪随机数流 2.方法(Random的方法有很多,在此只解释说明我认为比较常用的几个方法) (1)next(int bits):生成下一个伪随机数 (2)nextDouble ...
随机推荐
- JavaScript函数式编程究竟是什么?
摘要: 理解函数式编程. 作者:前端小智 原文:JS中函数式编程基本原理简介 Fundebug经授权转载,版权归原作者所有. 在长时间学习和使用面向对象编程之后,咱们退一步来考虑系统复杂性. 在做了一 ...
- sparkSQL中的example学习(3)
UserDefinedTypedAggregation.scala(用户可自定义类型) import org.apache.spark.sql.expressions.Aggregator impor ...
- 003-OpenStack-镜像服务
OpenStack-镜像服务 [基于此文章的环境]点我快速打开文章 1.安装和配置 控制节点(controller) 1.1 创库授权 glance mysql CREATE DATABASE gla ...
- 笔记6:Django基础
Django-MVT (1)查看python版本号: python -m django --version (2) 创建Django项目 django-admin startproject mysit ...
- Rust中的错误处理
Result & Panic 这次讲得详细,从错误的来历及简写过程, 都写明白了, 先浅,再深,先深,再浅, 反复之, 学习王道~ use std::fs::File; //use std:: ...
- 两种方式实现浅拷贝 for in实现和Object.assign({}, 对象1, 对象2);
浅拷贝只拷贝对象的一层,如果对象的属性还是对象,那么user3和user4这两个对象对应的值都会发生改变 // 拷贝分为浅拷贝和深拷贝. // 浅拷贝的实现 通过for in实现 var user1 ...
- @TableField(select=false)
使用这个注解排除删除标识字段.
- 201871010123-吴丽丽《面向对象程序设计(Java)》第十五周学习总结
201871010123-吴丽丽<面向对象程序设计(Java)>第十五周学习总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ ...
- Linux环境下sudo切换用户后执行其他命令
https://blog.csdn.net/liangxw1/article/details/80106465
- tf.tile() 用法介绍
tile() 平铺之意,用于在同一维度上的复制 tile( input, #输入 multiples, #同一维度上复制的次数 name=None ...