原子类

一、什么是原子类&作用

  • 不可分割

  • 一个操作是不可中断的,即便是多线程的情况下也可以保证

  • java.util.concurrent.atomic


  • 作用类似与锁,为保证并发情况下线程安全。原子类相比锁更具有优势

    • 粒度更细:

      原子变量可以把竞争范围缩小到变量级别,这是我们可以获得的最细粒度的情况,通常锁的粒度都要比原子变量的粒度大

    • 效率更高:

      通常,使用原子类的效率会比使用锁的效率更高,除了高度竞争的情况


二、原子类纵览


三、Atomic*基本类型原子类

包含:AtomicInteger、AtomicLong、AtomicBoolean

以AtomicInteger为例子

1、常用方法

  • public final int get() // 获取当前的值

  • public final int getAndSet(int newValue) // 获取当前的值,并设置新的值

  • public final int getAndIncrement() //获取当前的值,并自增

  • public final int getAndDecrement() // 获取当前的值,并自减

  • public final int getAndAdd(int delta) // 获取当前的值,并加上预期的值

  • boolean compareAndSet(int expect,int update) // 如果输入的数字等于预期值,则以原子方式将该值设置为输入值(update)

  • 代码演示: 原子类和普通类的对比

/******@author 阿昌@create 2021-06-12 18:04********      演示AtomicInteger的基本用法,并对比非原子类的线程安全问题*/
public class AtomicIntegerDemo1 implements Runnable {private static final AtomicInteger atomicInteger =  new AtomicInteger();//原子类型自增public void atomicIncrement(){atomicInteger.getAndIncrement();}private static volatile int basicCount = 0;//普通类型自增public void basicIncrement(){basicCount++;}@Overridepublic void run() {for (int i = 0; i < 10000; i++) {atomicIncrement();basicIncrement();}}//主函数public static void main(String[] args) throws InterruptedException {AtomicIntegerDemo1 aid = new AtomicIntegerDemo1();Thread thread1 = new Thread(aid);Thread thread2 = new Thread(aid);thread1.start();thread2.start();thread1.join();thread2.join();System.out.println("原子类的结果:"+atomicInteger.get());System.out.println("普通变量值:"+basicCount);}
}

  • 给普通类型增加synchronized修饰


  • 代码演示: getAndAdd()


四、Atomic*Array数组类型原子类

/******@author 阿昌@create 2021-06-12 18:17********      演示原子数组的使用方法*/
public class AtomicArray {public static void main(String[] args) {AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(1000);Incrementer incrementer = new Incrementer(atomicIntegerArray);Decrementer decrementer = new Decrementer(atomicIntegerArray);Thread[] threadsIncrementer = new Thread[100];Thread[] threadsDecrementer = new Thread[100];for (int i = 0; i < 100; i++) {threadsDecrementer[i] = new Thread(decrementer);threadsIncrementer[i] = new Thread(incrementer);threadsDecrementer[i].start();threadsIncrementer[i].start();}//        Thread.sleep(10000);for (int i = 0; i < 100; i++) {try {threadsDecrementer[i].join();threadsIncrementer[i].join();} catch (InterruptedException e) {e.printStackTrace();}}for (int i = 0; i <atomicIntegerArray.length() ; i++) {if (atomicIntegerArray.get(i)!=0){System.out.println("发现了错误: " +i);}}System.out.println("运行结束");}
}//自减任务类
class Decrementer implements Runnable{private AtomicIntegerArray array;public Decrementer(AtomicIntegerArray array) {this.array = array;}@Overridepublic void run() {for (int i = 0; i < array.length(); i++) {array.getAndDecrement(i);}}
}//自增任务类
class Incrementer implements Runnable{private AtomicIntegerArray array;public Incrementer(AtomicIntegerArray array) {this.array = array;}@Overridepublic void run() {for (int i = 0; i < array.length(); i++) {array.getAndIncrement(i);}}
}


五、Atomic*Reference引用类型原子类

  • 源码,compareAndSet()

  • 这里是之前的自旋锁演示例子
/******@author 阿昌@create 2021-06-11 21:10********      自旋锁演示*/
public class SpinLock {private AtomicReference<Thread> sign = new AtomicReference<>();//加锁操作public void lock(){Thread current = Thread.currentThread();//期待是null,如果是期望的,就将其设置为currentwhile (!sign.compareAndSet(null,current)){System.out.println(Thread.currentThread().getName()+":自旋获取失败,再次尝试");}}//解锁操作public void unlock(){Thread current = Thread.currentThread();//期待加锁的当前线程,如果是期望的,就将其设置为为null,也就是没有持有了,就是解锁了sign.compareAndSet(current,null);}public static void main(String[] args) {SpinLock spinLock = new SpinLock();Runnable runnable = new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + ":开始尝试获取自旋锁");spinLock.lock();System.out.println(Thread.currentThread().getName() + ":获取到了自旋锁");try {Thread.sleep(300);} catch (InterruptedException e) {e.printStackTrace();} finally {spinLock.unlock();System.out.println(Thread.currentThread().getName() + ":释放了自旋锁");}}};Thread thread1 = new Thread(runnable);Thread thread2 = new Thread(runnable);thread1.start();thread2.start();}}

六、AtomicIntegerFieldUpdater升级原子操作

  • AtomicIntegerFieldUpdater对普通变量进行升级

  • 使用场景

    • 偶尔需要一个原子get/set操作(如晚上某个时刻他存在大量并发修改,其他时刻就正常)
    • 这个变量我们无法操作,只能对他进行升级
  • 代码演示

/******@author 阿昌@create 2021-06-12 19:02********      演示AtomicIntegerFieildUpdater的用法*/
public class AtomicIntegerFieildUpdater implements Runnable {static Candidate tom;static Candidate jack;//newUpdater():参数1指定哪个类,参数2哪个属性public static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater.newUpdater(Candidate.class,"score");@Overridepublic void run() {for (int i = 0; i < 10000; i++) {tom.score++;//普通自增scoreUpdater.getAndIncrement(jack);//通过包装自增}}//候选人类public static class Candidate{//分数volatile int score;}//主函数public static void main(String[] args) throws InterruptedException {tom = new Candidate();jack = new Candidate();AtomicIntegerFieildUpdater a = new AtomicIntegerFieildUpdater();Thread thread1 = new Thread(a);Thread thread2 = new Thread(a);thread1.start();thread2.start();thread1.join();thread2.join();System.out.println("普通自增: "+tom.score);System.out.println("升级自增: "+jack.score);}}

升级后的操作,都会直接作用到原来对象的属性上:所以直接 jack.score就可


他让我们传入类,和对应属性名,这里就可以感觉到他使用的底层原理是反射

  • 注意点

    • 不支持被static修饰的变量
    • 可见范围,由public修饰的变量,private不行

七、Adder累加器

  • Java8引入

  • 高并发下LongAdder比AtomicLong效率高,本质还是空间换时间

  • 竞争激烈的情况下,LongAdder会把不同线程对应到不同的Cell上进行修改,降低冲突的概率,是多段锁的理念,提高了并发性

1、对比AddderLong & AtomicLong的高并发性能

  • AtomicLong,20个线程并发,每个线程执行10000次

    public class AtomicLongDemo {//主函数public static void main(String[] args) throws InterruptedException {AtomicLong counter = new AtomicLong(0);//新建线程池ExecutorService pool = Executors.newFixedThreadPool(20);long startTime = System.currentTimeMillis();//任务次数for (int i = 0; i < 10000; i++) {pool.submit(new Task(counter));}//关闭线程池pool.shutdown();while (!pool.isTerminated()){}long endTime = System.currentTimeMillis();System.out.println(counter.get());System.out.println("AtomicLong完成时间:"+(endTime-startTime)+"毫秒");}//任务内部类public static class Task implements Runnable{private AtomicLong count;public Task(AtomicLong count) {this.count = count;}@Overridepublic void run() {for (int i = 0; i < 10000; i++) {count.incrementAndGet();//自增}}}}
    

    花费:1.959s


  • LongAdder,20个线程并发,每个线程执行10000次

    public class LongAdderDemo {//主函数public static void main(String[] args) throws InterruptedException {LongAdder counter = new LongAdder();//新建线程池ExecutorService pool = Executors.newFixedThreadPool(20);long startTime = System.currentTimeMillis();//任务次数for (int i = 0; i < 10000; i++) {pool.submit(new Task(counter));}//关闭线程池pool.shutdown();while (!pool.isTerminated()){}long endTime = System.currentTimeMillis();System.out.println(counter.sum());System.out.println("LongAdder完成时间:"+(endTime-startTime)+"毫秒");}//任务内部类public static class Task implements Runnable{private LongAdder count;public Task(LongAdder count) {this.count = count;}@Overridepublic void run() {for (int i = 0; i < 10000; i++) {count.increment();//自增}}}}
    

    花费:0.373s

总结:在多线程的情况下,LongAdder比AtomicLong的性能更好


2、为什么AdderLong高并发性能好的原因

  • AtomicLong每次加法,都需要flush和refresh,导致消耗资源更多。


  • LongAdder,每个线程他自己有独立的计数器

那这里我就觉得就会出现最后线程不安全的情况,无法保持一致性,那这里就要讲一下他最后的Sum汇总阶段


3、Sum源码分析

他最后会判断,如果有as变量也就是cell[]数组,他就跟base一起相加结果返回最后的值

上面的源码看出,这个遍历相加的内部没有保证线程安全,也就是说如果之前加好的数组元素发生了变动,他就不会实时最新的反应在最终返回的sum总和中,也就是说返回的sum结果可能不是最新的值


4、AtomicLong & LongAdder对比

  • LongAdder

    • 消耗更多的空间;
    • 在高并发的情况下性能更好;
    • 适用于统计求和计数场景;
    • 类方法相对较少


八、Accumulator累加器

  • 类似与LongAdder,功能更强劲

  • 代码演示:基本用法

public class LongAccumulatorDemo {public static void main(String[] args) {//参数1:表达式//参数2:初始值,对X的第一次定义//最开始会将初始值赋给X ,y就是之前的结果;类似于 数学归纳法LongAccumulator accumulator = new LongAccumulator((x, y) -> x + y, 100);accumulator.accumulate(1);//此时,x=1,y=100,结果为101accumulator.accumulate(2);//此时,x=2,y=101,结果为103System.out.println(accumulator.getThenReset());}
}

  • 灵活使用,自定义表达式: 求1加到9中最大的数
public class LongAccumulatorDemo {public static void main(String[] args) {//求1加到9中最大的数LongAccumulator accumulator = new LongAccumulator((x, y) -> Math.max(x,y), 0);ExecutorService pool = Executors.newFixedThreadPool(10);//从1加到9IntStream.range(1,10).forEach(i->pool.submit(()->accumulator.accumulate(i)));pool.shutdown();while (!pool.isTerminated()){ }System.out.println(accumulator.getThenReset());}
}


  • 使用场景

    • 并行计算,且不要求计算有顺序

Day296.原子类 -Juc相关推荐

  1. 并发笔记(八)JUC原子类以及线程池(Executors)

    一.原子类 JUC中提供了针对一个变量的修改的简单原子操作,提供了原子类,相对于我们自己采用锁的方式实现来说,原子类的性能更好. 1.1原子类的底层实现原理理论:volatile+(循环的CAS) C ...

  2. 6.Atomic原子类

    Atomic原子类 JUC包下提供的原子类底层的实现原理基本都是差不多的,都是基于volatile和CAS操作来保证线程安全的. AtomicInteger.AtomicLong整型的原子类 Atom ...

  3. 【Java_多线程并发编程】JUC原子类——4种原子类

    根据修改的数据类型,可以将JUC包中的原子操作类可以分为4种,分别是: 1. 基本类型: AtomicInteger, AtomicLong, AtomicBoolean ; 2. 数组类型: Ato ...

  4. Java多线程系列--“JUC原子类”01之 框架

    2019独角兽企业重金招聘Python工程师标准>>> Java多线程系列--"JUC原子类"01之 框架 根据修改的数据类型,可以将JUC包中的原子操作类可以分 ...

  5. Java多线程系列--“JUC原子类”03之 AtomicLongArray原子类

    概要 AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray这3个数组类型的原子类的原理和用法相似.本章以AtomicLongArray对数 ...

  6. 6.juc包下的原子类AtomicInteger,AtomicLong等AtomicXXX介绍

     在介绍juc中的原子类之前,先看看官方文档对java.util.concurrent.atomic包的介绍官方文档地址这里截取翻译之后的部分描述 1. 支持对单个变量进行无锁线程安全编程 2. 类的 ...

  7. JUC多线程:Atomic原子类与CAS原理

    一.Atomic 原子类的原理: Atomic 原子操作类是基于无锁 CAS + volatile 实现的,并且类中的所有方法都使用 final 修饰,进一步保证线程安全.而 CAS 算法的具体实现方 ...

  8. JUC原子类-数组类型(三)

    AtomicLongArray介绍: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray这3个数组类型的原子类的原理和用法相似.本章以A ...

  9. java 原子数据类型_java并发编程(十一)----(JUC原子类)基本类型介绍

    上一节我们说到了基本原子类的简单介绍,这一节我们先来看一下基本类型: AtomicInteger, AtomicLong, AtomicBoolean.AtomicInteger和AtomicLong ...

  10. juc原子类之五:AtomicLongFieldUpdater原子类

    概要 AtomicIntegerFieldUpdater, AtomicLongFieldUpdater和AtomicReferenceFieldUpdater这3个修改类的成员的原子类型的原理和用法 ...

最新文章

  1. poj 2352 线段树
  2. Django扩展xadmin后台管理
  3. 用移位实现除以或者乘以一个数
  4. linux pid t 头文件_linux系统调用相关头文件
  5. Eclipse 创建 Maven 工程
  6. 汇编指令push,mov,call,pop,leave,ret建立与释放栈的过程
  7. 基于GPU的粒子系统
  8. 思维导图的使用场合有哪些?怎么画思维导图
  9. 如何在linux下安装一个音乐播放器
  10. 普通路由器DMZ主机设置及访问方法
  11. 自定义View时,用到Paint Canvas的一些温故,PropertyAnimation中的ObjectAnimator(动画三,“大大姐”的旋转跳跃)...
  12. C语言渔夫打鱼晒网问题
  13. echarts中折线图、柱状图之间的转换
  14. 学会这招,妈妈再也不担心我的排名上不去了
  15. 可汗学院的数学从零开始学习顺序?
  16. 数据库卸载后安装不成功的问题
  17. Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] ...
  18. 简约二次元网址导航发布页HTML源码
  19. 你还在担心用户的真实号码被泄露?号码隐私保护服务PNS来了
  20. 超级浏览器怎么使用,1分钟讲清楚

热门文章

  1. 20220525商汤算法岗实习面试经历
  2. 北航提出基于语言桥接的时空交互来进行准确指向性视频对象分割
  3. [Atlassian]JiraConfluenceCrowd配置SSO
  4. 网吧用计算机性能配件清单,如何查看网吧电脑配置清单图文教程
  5. 中级程序员还应该如何提高自己
  6. 愿守内心宁静,砥砺此生修行
  7. rtx服务器插件的作用,腾讯通RTX手机版插件介绍 - 有度帮助中心
  8. 电影《检察风云》投资价值简单分析
  9. html 大转盘游戏,HTML5 Canvas大转盘抽奖活动页面代码
  10. python constants_Python constants.SUCCESS属性代码示例