并发编程-JUC-原子类

这篇具有很好参考价值的文章主要介绍了并发编程-JUC-原子类。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

JUC 整体概览

并发编程-JUC-原子类,Java 多线程与并发,Java,CAS,ABA,原子类

原子类

  • 基本类型-使用原子的方式更新基本类型
    • AtomicInteger:整形原子类
    • AtomicLong:长整型原子类
    • AtomicBoolean :布尔型原子类
  • 引用类型
    • AtomicReference:引用类型原子类
    • AtomicStampedReference:原子更新引用类型里的字段原子类
    • AtomicMarkableReference :原子更新带有标记位的引用类型
  • 数组类型-使用原子的方式更新数组里的某个元素
    • AtomicIntegerArray:整形数组原子类
    • AtomicLongArray:长整形数组原子类
    • AtomicReferenceArray :引用类型数组原子类
  • 对象的属性修改类型
    • AtomicIntegerFieldUpdater:原子更新整形字段的更新器
    • AtomicLongFieldUpdater:原子更新长整形字段的更新器
    • AtomicReferenceFieldUpdater :原子更新引用类形字段的更新器
  • JDK1.8新增类
    • DoubleAdder:双浮点型原子类
    • LongAdder:长整型原子类
    • DoubleAccumulator:类似DoubleAdder,但要更加灵活(要传入一个函数式接口)
    • LongAccumulator:类似LongAdder,但要更加灵活(要传入一个函数式接口)

原子类实现原理CAS

CAS其实就是执行一个函数:CAS(V,E,N)直到成功为止。

CAS操作涉及到三个操作数:
V:要读写的内存地址
E:进行比较的值 (预期值)这个期望值是每次自旋的时候从V中读取出来,然后更新的时候再比较这个值和V中的值是否相等如果相等表示没有其它线程来修改,否则被修改过,继续自旋。直到成功为止。
N:拟写入的新值

// 以AtomicInteger为例
public final int getAndAddInt(Object var1, long var2, int var4) {
       int var5;
       do {
           var5 = this.getIntVolatile(var1, var2);
       } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

       return var5;
   }

compareAndSwapInt是一个native方法:

public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe,
jobject obj, jlong offset, jint e, jint x))
UnsafeWrapper("Unsafe_CompareAndSwapInt");
oop p = JNIHandles::resolve(obj);//将Java对象解析成JVM的oop(普通对象指针)
//根据对象p内存地址和内存地址偏移量计算拟修改对象属性的地址
jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
//基于cas比较并替换,x表示拟更新的值, addr表示要操作的内存地址, e表示预期值 这是一个原子的方法
return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END

CAS存在的缺陷

主要存在三个缺陷:循环时间太长、只能保证一个共享变量原子操作、ABA问题。

  • 循环时间太长:如果CAS一直不成功呢?如果自旋CAS长时间地不成功,则会给CPU带来非常大的开销。
    • 原子类AtomicInteger#getAndIncrement()的方法
  • 只能保证一个共享变量原子操作:看了CAS的实现就知道这只能针对一个共享变量,如果是多个共享变量就只能使用锁了。
  • ABA问题:CAS需要检查操作值有没有发生改变,如果没有发生改变则更新。但是存在这样一种情况:如果一个值原来是A,变成了B,然后又变成了A,那么在CAS检查的时候会发现没有改变,但是实质上它已经发生了改变,这就是所谓的ABA问题。

ABA问题演示

public static void main(String[] args) {
      AtomicInteger at = new AtomicInteger(100);
      AtomicStampedReference<Integer> st = new AtomicStampedReference(100, 1);
      new Thread(() -> {
          // 值增1
          int i = at.incrementAndGet();
          System.out.println("AtomicInteger after modify : " + i);
          // 撤回修改 模拟 A -> B
          int j = at.decrementAndGet();
          System.out.println("AtomicInteger after modify redo : " + j);
          // 值增1 版本号 + 1 为 2
          st.compareAndSet(st.getReference(), st.getReference() + 1, st.getStamp(), st.getStamp() + 1);
          System.out.println("AtomicStampedReference after modify : " + i + " version is : " + st.getStamp());
          // 撤回修改 模拟 A -> B 只不过这个时候我们携带了一个版本号 版本号 + 1 为 3
          st.compareAndSet(st.getReference(), st.getReference() - 1, st.getStamp(), st.getStamp() + 1);
          System.out.println("AtomicStampedReference after modify  redo : " + j + " version is : " + st.getStamp());
      }).start();
      new Thread(() -> {
          // 更新为102可以成功 这个数据其实是被改过一次
          at.compareAndSet(at.get(), at.get() + 2);
          System.out.println("AtomicInteger after modify in other Thread: " + at.get());
          // 更新为102不可以成功 获取的还是100 因为我们预期的版本号是1 也就是第一次的100 所以修改不成功
          st.compareAndSet(st.getReference(), st.getReference() + 2, 1, st.getStamp() + 1);
          // 没有修改成功所以版本号还是 3
          System.out.println("AtomicStampedReference after modify : " + st.getReference() + " version is : " + st.getStamp());
      }).start();
  }

并发编程-JUC-原子类,Java 多线程与并发,Java,CAS,ABA,原子类文章来源地址https://www.toymoban.com/news/detail-831323.html

到了这里,关于并发编程-JUC-原子类的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 并发编程-JUC-原子类

    JUC 整体概览 原子类 基本类型-使用原子的方式更新基本类型 AtomicInteger:整形原子类 AtomicLong:长整型原子类 AtomicBoolean :布尔型原子类 引用类型 AtomicReference:引用类型原子类 AtomicStampedReference:原子更新引用类型里的字段原子类 AtomicMarkableReference :原子更新带有标记位的引

    2024年02月21日
    浏览(40)
  • JUC并发编程之原子类

    目录 1. 什么是原子操作 1.1 原子类的作用 1.2 原子类的常见操作 原子类的使用注意事项 并发编程是现代计算机应用中不可或缺的一部分,而在并发编程中,处理共享资源的并发访问是一个重要的问题。为了避免多线程访问共享资源时出现竞态条件(Race Condition)等问题,J

    2024年02月13日
    浏览(50)
  • JUC并发编程学习笔记(十九)原子引用

    带版本号的原子操作! 解决ABA问题,引入原子引用(乐观锁思想) AtomicStampedReference类解决ABA问题 所有相同类型的包装类对象之间值的比较全部使用equals方法比较 Integer使用了对象缓存机制,默认范围是-128至127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为v

    2024年02月05日
    浏览(56)
  • Java多线程(3)---锁策略、CAS和JUC

    目录 前言 一.锁策略 1.1乐观锁和悲观锁 ⭐ 两者的概念 ⭐实现方法 1.2读写锁  ⭐概念 ⭐实现方法 1.3重量级锁和轻量级锁 1.4自旋锁和挂起等待锁 ⭐概念 ⭐代码实现 1.5公平锁和非公平锁 1.6可重入锁和不可重入锁 二.CAS 2.1为什么需要CAS 2.2CAS是什么 ⭐CAS的介绍 ⭐CAS工作原理

    2024年02月13日
    浏览(70)
  • 3.多线程之JUC并发编程0

    1.学习方法 java1.8才有juc 面试高频问 源码+官方文档 //怎么用多线程,如果是没有返回结果的就单独开一条线程,或者使用线程池,如果有返回值的就用callable 2.什么是juc 三个包 java.util.concurrent java.util.concurrent.atomic java.util.concurrent.locks 介绍 Callable性能比Runnable高,并且Runnable没有返

    2024年02月08日
    浏览(51)
  • 5.多线程之JUC并发编程2

    1.CompletableFuture异步回调 像ajax,未来再得到执行结果,想服务器不分先后顺序执行,可以用异步回调 2.JMM 面试:对 Volatile的理解 答: Volatile是jvm通过轻量级的同步机制,比sychronized更轻 1.保证可见性 2.不保证原子性 3.禁止指令重排 什么是JMM? 答:java内存模型,是一种规定,不存在的东西

    2024年02月08日
    浏览(40)
  • JUC并发编程学习笔记(十)线程池(重点)

    线程池:三大方法、七大参数、四种拒绝策略 池化技术 程序的运行,本质:占用系统的资源!优化资源的使用!- 池化技术(线程池、连接池、对象池......);创建和销毁十分消耗资源 池化技术:事先准备好一些资源,有人要用就拿,拿完用完还给我。 线程池的好处: 1、

    2024年02月06日
    浏览(46)
  • JUC并发编程学习笔记(一)认知进程和线程

    进程 一个程序,如QQ.exe,是程序的集合 一个进程往往可以包含多个线程,至少包含一个 java默认有两个线程,GC垃圾回收线程和Main线程 线程:一个进程中的各个功能 java无法真正的开启线程,因为java是运行在虚拟机上的,所以只能通过C++,通过native本地方法调用C++开启线程

    2024年02月06日
    浏览(55)
  • JUC并发编程-集合不安全情况以及Callable线程创建方式

    1)List 不安全 ArrayList 在并发情况下是不安全的 解决方案 : 1.Vector 2.Collections.synchonizedList() 3. CopyOnWriteArrayList 核心思想 是,如果有 多个调用者(Callers)同时要求相同的资源 (如内存或者是磁盘上的数据存储),他们 会共同获取相同的指针指向相同的资源 , 直到某个调用者

    2024年01月23日
    浏览(49)
  • 【Java 并发编程】CAS 原理解析

    悲观锁 的原理是每次实现数据库的增删改的时候都进⾏阻塞,防⽌数据发⽣脏读。 乐观锁 的原理是在数据库更新的时候,⽤⼀个 version 字段来记录版本号,然后通过⽐较是不是⾃⼰要修改的版本号再进⾏修改。这其中就引出了⼀种⽐较交换的思路来实现数据的⼀致性,事实

    2024年02月06日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包