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

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. 数字,字符串,time模块,文本进度条

    数字和字符串 数字类型 整形 整数, 1/2/3/12/2019 整形用来描述什么, 身高/年龄/体重 age = 18 height = 180 浮点型 浮点数,小数 salary = 10 pri ...

  2. 如何去掉任务栏的IE搜索栏

    在IE图标的位置单击鼠标右键,选择退出.

  3. 0.Jenkins 介绍

    一.持续集成的概念 continuous  intergaration  (简称CI),持续集成. 持续集成是一种软件开发实践,即团队开发成员经常集成他们的工作,通常每个成员每天至少集成一次,也就意味 ...

  4. 201871010108-高文利《面向对象程序设计(java)》第十五周学习总结

    项目 内容 这个作业属于哪个课程 <任课教师博客主页链接> https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 <作业链接地址> ht ...

  5. Lodop打印表格带页头页尾 高度是否包含页头页尾 转载

    通过设置TableHeightScope,可以实现对ADD_PRINT_TABLE,表格带页头页尾,查看本博客另一篇博文:Lodop打印表格带页头页尾 自动分页每页显示头尾 超文本超过打印项高度,会自 ...

  6. day13_7.15 迭代器和生成器

    1.迭代器 迭代就是一个更新换代的过程,每次迭代都必须基于上一次的结果. 迭代器就是迭代取值的工具.举个例子: while True: print('循环输出') 此代码会无限循环输出文字,是个死循环 ...

  7. zz深度学习目标检测2014至201901综述

    论文学习-深度学习目标检测2014至201901综述-Deep Learning for Generic Object Detection A Survey  发表于 2019-02-14 |  更新 ...

  8. leetcode138. 复制带随机指针的链表

    思路一:哈希 借助哈希保存节点信息. 代码 时间复杂度:O(n)空间复杂度:O(n) class Solution{ public: Node* copyRandomList(Node* head) ...

  9. nwjs-简单应用于基本结构

    简单应用 1.在解压目录下创建文件夹dmeo01 2.在文件夹中创建index.html和package.json两个文件 3.index.html内容: <!DOCTYPE html> ...

  10. 在树莓派上配置MariaDB

    在树莓派上配置MariaDB 前言 MariaDB是由原本开发MySQL的一些原始开发者领导,他们担心Oracle收购MySQL后会有一些隐患.MariaDB与MySQL保持这高度兼容性,并使用了一个 ...