原子操作类AtomicInteger详解
为什么需要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的数字。这是为什么呢?
要是换成volatile修饰count变量呢?
顺带说下volatile关键字很重要的两个特性:
1、保证变量在线程间可见,对volatile变量所有的写操作都能立即反应到其他线程中,换句话说,volatile变量在各个线程中是一致的(得益于java内存模型—"先行发生原则");
2、禁止指令的重排序优化;
那么换成volatile修饰count变量后,会有什么效果呢? 试一试:
public class AtomicIntegerTest {
private static final int THREADS_CONUT = 20;
public static volatile 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);
}
}
结果似乎又失望了,测试结果和上面的一致,每次都是输出小于20000的数字。这又是为什么么? 上面的论据是正确的,也就是上面标红的内容,但是这个论据并不能得出"基于volatile变量的运算在并发下是安全的"这个结论,因为核心点在于java里的运算(比如自增)并不是原子性的。
用了AtomicInteger类后会变成什么样子呢?
把上面的代码改造成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()方法的原子性。
非阻塞同步
同步:多线程并发访问共享数据时,保证共享数据再同一时刻只被一个或一些线程使用。
我们知道,阻塞同步和非阻塞同步都是实现线程安全的两个保障手段,非阻塞同步对于阻塞同步而言主要解决了阻塞同步中线程阻塞和唤醒带来的性能问题,那什么叫做非阻塞同步呢?在并发环境下,某个线程对共享变量先进行操作,如果没有其他线程争用共享数据那操作就成功;如果存在数据的争用冲突,那就才去补偿措施,比如不断的重试机制,直到成功为止,因为这种乐观的并发策略不需要把线程挂起,也就把这种同步操作称为非阻塞同步(操作和冲突检测具备原子性)。在硬件指令集的发展驱动下,使得 "操作和冲突检测" 这种看起来需要多次操作的行为只需要一条处理器指令便可以完成,这些指令中就包括非常著名的CAS指令(Compare-And-Swap比较并交换)。《深入理解Java虚拟机第二版.周志明》第十三章中这样描述关于CAS机制:
图取自《深入理解Java虚拟机第二版.周志明》13.2.2
所以再返回来看AtomicInteger.incrementAndGet()方法,它的时间也比较简单
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
incrementAndGet()方法在一个无限循环体内,不断尝试将一个比当前值大1的新值赋给自己,如果失败则说明在执行"获取-设置"操作的时已经被其它线程修改过了,于是便再次进入循环下一次操作,直到成功为止。这个便是AtomicInteger原子性的"诀窍"了,继续进源码看它的compareAndSet方法:
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
可以看到,compareAndSet()调用的就是Unsafe.compareAndSwapInt()方法,即Unsafe类的CAS操作。
使用示例如下图,用于标识程序执行过程中是否发生了异常,使用quartz实现高级定制化定时任务(包含管理界面)实现中:
---------------------
作者:simonsfan
来源:CSDN
原文:https://blog.csdn.net/fanrenxiang/article/details/80623884
版权声明:本文为博主原创文章,转载请附上博文链接!
原子操作类AtomicInteger详解的更多相关文章
- Java原子操作类AtomicInteger应用场景
Java中有那么一些类,是以Atomic开头的.这一系列的类我们称之为原子操作类.以最简单的类AtomicInteger为例.它相当于一个int变量,我们执行Int的 i++ 的时候并不是一个原子操作 ...
- qml学习笔记(二):可视化元素基类Item详解(上半场anchors等等)
原博主博客地址:http://blog.csdn.net/qq21497936本文章博客地址:http://blog.csdn.net/qq21497936/article/details/78516 ...
- UML类图详解_关联关系_一对多
对于一对多的示例,可以想象一个账户可以多次申购.在申购的时候没有固定上限,下限为0,那么就可以使用容器类(container class)来搞,最常见的就是vector了. 下面我们来看一个“一对多” ...
- UML类图详解_关联关系_多对一
首先先来明确一个概念,即多重性.什么是多重性呢?多重性是指两个对象之间的链接数目,表示法是“下限...上限”,最小数据为零(0),最大数目为没有设限(*),如果仅标示一个数目级上下限相同. 实际在UM ...
- Android中Application类的详解:
Android中Application类的详解: 我们在平时的开发中,有时候可能会须要一些全局数据.来让应用中的全部Activity和View都能訪问到.大家在遇到这样的情况时,可能首先会想到自定义一 ...
- Delphi中的线程类 - TThread详解
Delphi中的线程类 - TThread详解 2011年06月27日 星期一 20:28 Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大多数Delphi书藉都有说到,但基本 ...
- UML简单介绍—类图详解
类图详解 阅读本文前请先阅读:UML简单介绍—类图这么看就懂了 1.泛化关系 一个动物类: /** * 动物类 */ public class Animal { public String name; ...
- Properties类使用详解
Java Properties类使用详解 概述 Properties 继承于 Hashtable.表示一个持久的属性集,属性列表以key-value的形式存在,key和value都是字符串. Pr ...
- 在java poi导入Excel通用工具类示例详解
转: 在java poi导入Excel通用工具类示例详解 更新时间:2017年09月10日 14:21:36 作者:daochuwenziyao 我要评论 这篇文章主要给大家介绍了关于在j ...
随机推荐
- dbcp的销毁
使用commons-dbcp-1.2.2.jar的DataSource,发现每次动态编译后连接池中的连接不会释放,新的连接池建立有mssql多出一组连接,只有重新启动tomcat或weblogic才可 ...
- POJ 2546 Circular Area(两个圆相交的面积)
题目链接 题意 : 给你两个圆的半径和圆心,让你求两个圆相交的面积大小. 思路 : 分三种情况讨论 假设半径小的圆为c1,半径大的圆为c2. c1的半径r1,圆心坐标(x1,y1).c2的半径r2,圆 ...
- Cannot evaluate the property expression "$([MSBuild]::ValueOrDefault('$(VCTargetsPath)','$(MSBuildExtensionsPath32)\Microsoft.Cpp\v4.0\V140\'))" found at "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuil
Cannot evaluate the property expression "$([MSBuild]::ValueOrDefault('$(VCTargetsPath)','$(MSBu ...
- Alpha冲刺(七)
Information: 队名:彳艮彳亍团队组长博客:戳我进入作业博客:班级博客本次作业的链接 Details: 组员1(组长)柯奇豪 过去两天完成了哪些任务 改用更易用的springboot+myb ...
- atan2()如何转换为角度
atan2()如何转换为角度 Math.atan2()函数返回点(x,y)和原点(0,0)之间直线的倾斜角.那么如何计算任意两点间直线的倾斜角呢?只需要将两点x,y坐标分别相减得到一个新的点(x2-x ...
- 基于ZKEACMS的.Net Core多租户CMS建站系统
多租户架构 多租户技术或称多重租赁技术,简称SaaS,是一种软件架构技术,是实现如何在多用户环境下共用相同的系统或程序组件,并且可确保各用户间数据的隔离性.简单讲:在一台服务器上运行单个应用实例,它为 ...
- orcal exists
Oracle使用了一个复杂的自平衡B-tree结构.通常,通过索引查询数据比全表扫描要快.当 Oracle找出执行查询和Update语句的最好路径时,Oracle优化器将使用索引.同样在联结多个表时使 ...
- GPU大百科全书 第一章:美女 方程与几何
沉鱼落雁 前言:当你酣战在星际2的时候,或者当你在艾泽拉斯游历的时候,你有没有想过,眼前的这些绚丽的画面究竟是怎么来的呢?也许对大多数人来说,GPU对于图形的处理过程并不是那么重要,但总会有些人, ...
- PHP内核研究 静态变量
静态变量 它可以是 静态全局变量,如果不调用unset,那么这个静态变量会一直存在,直到程序退出时才由Zend内存管理来释放 它可以是 静态局部变量:在函数里定义,函数执行完后,该静态变量不会消失 它 ...
- [ActionScript 3.0] 透视投影
下面的示例演示如何使用透视投影来创建 3D 空间.该示例演示如何通过projectionCenter属性来修改消失点和更改空间的透视投影.进行这种修改后,将强制重新计算focalLength和fiel ...