感谢这两位博主的文章,文章源于:

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的更多相关文章

  1. List,Set,Map在java.util包下都是接口 List有两个实现类:ArrayList和LinkedList Set有两个实现类:HashSet和LinkedHashSet AbstractSet实现了Set

    List,Set,Map在java.util包下都是接口 List有两个实现类:ArrayList和LinkedListSet有两个实现类:HashSet和LinkedHashSetAbstractS ...

  2. java.io 包下的类有哪些 + 面试题

    java.io 包下的类有哪些 + 面试题 IO 介绍 IO 是 Input/Output 的缩写,它是基于流模型实现的,比如操作文件时使用输入流和输出流来写入和读取文件等. IO 分类 传统的 IO ...

  3. [Java多线程]-J.U.C.atomic包下的AtomicInteger,AtomicLong等类的源码解析

    Atomic原子类:为基本类型的封装类Boolean,Integer,Long,对象引用等提供原子操作. 一.Atomic包下的所有类如下表: 类摘要 AtomicBoolean 可以用原子方式更新的 ...

  4. 多线程爬坑之路-J.U.C.atomic包下的AtomicInteger,AtomicLong等类的源码解析

    Atomic原子类:为基本类型的封装类Boolean,Integer,Long,对象引用等提供原子操作. 一.Atomic包下的所有类如下表: 类摘要 AtomicBoolean 可以用原子方式更新的 ...

  5. Java 扫描包下所有类(包括jar包)

    package com.MyUtils.file; [java] view plain copy import java.io.File; import java.io.FileFilter; imp ...

  6. java.io包下适配和装饰模式的使用

    如java.io.LineNumberInputStream(deprecated),是装饰模式(decorate)的实现: 如java.io.OutputStreamWriter,是适配器模式(ad ...

  7. mongodb在java驱动包下的操作(转)

    推荐几章很有用的文章 java操作参考文档 http://www.cnblogs.com/hoojo/archive/2011/06/02/2068665.html http://blog.csdn. ...

  8. JAVA concurrent包下Semaphore、CountDownLatch等用法

    CountDownLatch 跟join的区别 CountDownLatch用处跟join很像,但是CountDownLatch更加灵活,如果子线程有多个阶段a.b.c; 那么我们可以实现在a阶段完成 ...

  9. 【第二周】关于java.util包下的Random类

    1.功能:此类的实例用于生成伪随机数流 2.方法(Random的方法有很多,在此只解释说明我认为比较常用的几个方法) (1)next(int bits):生成下一个伪随机数 (2)nextDouble ...

随机推荐

  1. odoo10学习笔记十一:视图综述

    转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/11189322.html 一:视图标签等公共结构 name (必选) 用于通过名字查找标签 model: 与v ...

  2. LINUX内核CPU负载均衡机制【转】

    转自:http://oenhan.com/cpu-load-balance 还是神奇的进程调度问题引发的,参看Linux进程组调度机制分析,组调度机制是看清楚了,发现在重启过程中,很多内核调用栈阻塞在 ...

  3. 深入理解defer(上)defer基础

    深入理解 defer 分上下两篇文章,本文为上篇,主要介绍如下内容: 为什么需要 defer: defer 语法及语义: defer 使用要点: defer 语句中的函数到底是在 return 语句之 ...

  4. sqlalchemy(2)

    orm介绍 orm英文全称object relational mapping,就是对象映射关系程序,简单来说我们类似python这种面向对象的程序来说一切皆对象,但是我们使用的数据库却都是关系型的,为 ...

  5. 启动hadoop后jps没有namenode,并且启动报错9000

    启动hadoop报错: 解决方法: 我发现没有9000端口被占用,也不知道9000到哪去了,但是也没有NameNode,于是,直接把NameNode格式化了,再重启HDFS即可. 格式化命令:

  6. UML系列

    UML类图:https://www.cnblogs.com/shindo/p/5579191.html UML用例图:https://www.jianshu.com/p/3cde67aed8e9 UM ...

  7. es6之后,真的不需要知道原型链了吗?

    3月份几乎每天都能看到面试的人从我身边经过,前段时间同事聊面试话题提到了原型链,顿时激起了我在开始学习前端时很多心酸的回忆.第一次接触js的面向对象思想是在读<js高程设计>(红宝书)的时 ...

  8. 「总结」插头$dp$

    集中做完了插头$dp$ 写一下题解. 一开始学的时候还是挺蒙的. 不过后来站在轮廓线$dp$的角度上来看就简单多了. 其实就是一种联通性$dp$,只不过情况比较多而已了. 本来转移方式有两种.逐行和逐 ...

  9. CF1149D Abandoning Roads(图论,最短路,状态压缩,最小生成树)

    题目大意:$n$ 个点,$m$ 条边的无向图,边权只有两种,小的为 $a$,大的为 $b$. 对于每个点 $p$,询问在这张图所有的最小生成树上,$1$ 到 $p$ 的最短距离的最小值. $2\le ...

  10. Spring Boot中的Mongodb多数据源扩展

    在日常工作中,我们通过Spring Data Mongodb来操作Mongodb数据库,在Spring Boot中只需要引入spring-boot-starter-data-mongodb即可. 然后 ...