一 什么是原子类?

所谓原子类必然是具有原子性的类,原子性操作--原子操作,百度百科中给的定义如下

"原子操作(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原子类的更多相关文章

  1. JUC 中的 Atomic 原子类总结

    1 Atomic 原子类介绍 Atomic 翻译成中文是原子的意思.在化学上,我们知道原子是构成一般物质的最小单位,在化学反应中是不可分割的.在我们这里 Atomic 是指一个操作是不可中断的.即使是 ...

  2. Atomic原子类

    Atomic原子类 Atomic原子类位于并发包java.util.concurrent下的java.util.concurrent.Atomic中. 1. 原子更新基本类型类 使用原子方式更新基本数 ...

  3. Java CAS同步机制 原理详解(为什么并发环境下的COUNT自增操作不安全): Atomic原子类底层用的不是传统意义的锁机制,而是无锁化的CAS机制,通过CAS机制保证多线程修改一个数值的安全性。

    精彩理解:  https://www.jianshu.com/p/21be831e851e ;  https://blog.csdn.net/heyutao007/article/details/19 ...

  4. Juc中Atomic原子类总结

    1 Atomic原子类介绍 2 基本类型原子类 3 数组类型原子类 4 引用类型原子类 5 对象的属性修改类型原子类

  5. (转)Java atomic原子类的使用方法和原理(一)

    在讲atomic原子类之前先看一个小例子: public class UseAtomic { public static void main(String[] args) { AtomicIntege ...

  6. Java线程--Atomic原子类使用

    原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/11871241.html Java线程--Atomic原子类使用 package concurr ...

  7. 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)

    前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...

  8. [Java多线程]-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)

    前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...

  9. JUC学习笔记--Atomic原子类

    J.U.C 框架学习顺序 http://blog.csdn.net/chen7253886/article/details/52769111 Atomic 原子操作类包 Atomic包 主要是在多线程 ...

随机推荐

  1. staticmethod classmethod property方法

    @staticmethod 静态方法 函数修饰符,用来修饰一个函数,类似于装饰器 class Dog(object): def __init__(self,name): self.name = nam ...

  2. 关于ubuntu安装软件的问题:apt-get和dpkg区别?

    两者的区别是dpkg绕过apt包管理数据库对软件包进行操作,所以你用dpkg安装过的软件包用apt可以再安装一遍,系统不知道之前安装过了,将会覆盖之前dpkg的安装.1.dpkg是用来安装.deb文件 ...

  3. Wampserver由橙变绿的解决过程

    因为C盘的内存问题,就重装了win7系统,那么就面临着很对软件要重新进行安装,安装wampserver时,再次遇到了服务器的图标一直是橙色的而不变绿色,安装包地址: http://download.c ...

  4. C语言中头文件怎么写?(本文来源网络,由黑乌鸦进一步完善)

      c语言头文件怎么写?我一直有这样的疑问,但是也一直没去问问到底咋回事:所以今天一定要把它弄明白! 其实学会写头文件之后可以为我们省去不少事情,可以避免书写大量的重复代码.有利于整理思路.使代码脉络 ...

  5. 单调队列 poj2823,fzu1894

    题目链接:http://poj.org/problem?id=2823 用RMQ超时了,我想应该是不会的,看discuss说,之前RMQ过了. 维护两个单调队列. 单调递减的队列,每插入一个时: 超过 ...

  6. BZOJ 3235: [Ahoi2013]好方的蛇

    BZOJ 3235: [Ahoi2013]好方的蛇 标签(空格分隔): OI-BZOJ OI-DP OI-容斥原理 Time Limit: 10 Sec Memory Limit: 64 MB Des ...

  7. P1909 买铅笔

    题目描述 P老师需要去商店买n支铅笔作为小朋友们参加NOIP的礼物.她发现商店一共有 33种包装的铅笔,不同包装内的铅笔数量有可能不同,价格也有可能不同.为了公平起 见,P老师决定只买同一种包装的铅笔 ...

  8. SpringBoot学习14:springboot异常处理方式4(使用SimpleMappingExceptionResolver处理异常)

    修改异常处理方法3中的全局异常处理Controller即可 package bjsxt.exception; import org.springframework.context.annotation ...

  9. iOS 实时监测网络状态(通过Reachability)

    在AppDelegate.m中 @property (nonatomic, strong) Reachability *reach; - (BOOL)application:(UIApplicatio ...

  10. 【杂题总汇】UVa-1336 Fixing the Great Wall

    [UVA-1336]Fixing the Great Wall 一开始把题看错了……直接用的整数存储答案:之后用double存最后输出答案的时候取整就AC了