JDK提供的原子类原理及使用

  • volatile只能保障可见性,不能保障原子性,如value++操作,就不是一个原子性操作,value++共分为以下三步操作(假如value的值是0):

    • 1、取出value的值为0;
    • 2、将value的值进行加一操作,得到一个新值为1;
    • 3、将新值1再赋值给变量value。
  • 假如线程1刚执行完了第二步,此时value的值依然为0,得到的新值为1,然后就轮到线程2执行。线程2执行第一步,取出value的值为0,再执行第二步,依然得到的新值为1,再执行第三步,将1赋值给value,执行完毕。此时线程1继续执行,又将1赋值给了value。所以在线程1和线程2执行完毕之后,执行了两次++操作,值本应该为2的value此时的值却为1,这时就出现了线程安全性问题。

  • 解决的办法就是让非原子性的++操作变成一个原子性操作即可,也就是说线程2必须等线程1三个步骤都执行完毕之后才能执行,通常我们都会使用synchronized同步代码块来保障++操作的原子性及内存的可见性

    public synchronized int getNext() {return value++;
    }
    
  • 由于synchronized会导致线程上下文的切换,性能并不高,所以我们可以使用JDK提供的原子类的方式来保障内存的可见性及变量操作的原子性(注:原子类底层是volatile和CAS共同作用的结果)。

    public class MainTest {private AtomicInteger value  = new AtomicInteger(0);public int getNext() {// value++return value.getAndIncrement();}public static void main(String[] args) {MainTest mainTest = new MainTest();Runnable r = new Runnable() {@Overridepublic void run() {while (true) {String threadName = Thread.currentThread().getName();System.out.println(threadName + " " + mainTest.getNext());try {Thread.sleep(100);} catch (InterruptedException e) {}}}};new Thread(r).start();new Thread(r).start();}
    }
    
  • 原子类源码路径(java.util.concurrent.atomic)

  • 原子类主要有以下的几种类型:

    • 原子更新基本类型(AtomicInteger、AtomicBoolean、AtomicLong)

      AtomicInteger value  = new AtomicInteger(0);
      value.getAndIncrement();
      
    • 原子更新数组(AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray)

      int [] s = {2,1,4,6};
      AtomicIntegerArray a = new AtomicIntegerArray(s);
      // 将角标为2的值进行加1操作
      a.getAndIncrement(2);
      // 将角标为2的值进行加10操作
      a.getAndAdd(2, 10);
      
    • 原子更新引用类型(AtomicReference)

      // 对user这个引用的get和set,保障对引用的原子性操作
      AtomicReference<User> user = new AtomicReference<>();
      
    • 原子更新字段(AtomicIntegerFieldUpdater,注:更新的字段不能使private,同时字段必须使用volatile进行修饰)

      // 对类字段的原子性操作
      AtomicIntegerFieldUpdater<User> old =  AtomicIntegerFieldUpdater.newUpdater(User.class, "old");
      User user = new User();
      System.out.println(old.getAndIncrement(user));
      
  • 原子类实现原子性操作的原理

    • 通过乐观锁(CAS,Compare And Swap)来实现,所谓乐观锁就是每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。

    • CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

    • 假如我们想对一个int型的变量value进行加1操作,总体的流程如下:

      • 1、先获取出value的值:int A = get();
      • 2、对value值进行加1操作:int B = A + 1;
      • 3、进行CAS操作:boolean flag = cas(A,B);
      • 4、假如flag为true,则返回 B,假如flag为false,则重复执行第一步操作,直到flag为true
    • AtomicInteger类中的getAndIncrement代码大致实现如下:

      public final int getAndIncrement() {int A, B;do {A = get();B = A + 1;} while (!compareAndSet(A, B));return B
      }
      
    • compareAndSet函数主要的操作如下:

      • 1、将A值和内存中的V值进行比较,若A != V,则返回false;

      • 2、若A == V,则将内存值更新为B,并返回true。

    • 从以上的步骤中我们也可以看出,整个过程的关键在于compareAndSet函数必须是一个原子性的操作,然而CAS也称为比较和交换,从字面的意义上我们可以得知CAS操作至少需要两步才能完成,所以compareAndSet函数也必须通过加锁来保障原子性。

    • 从JDK中的源码我们可以看到,compareAndSet并不是使用synchronized来保障原子性的,因为如果使用了synchronized的话,那么乐观锁将失去其自身的意义了。

    • compareAndSet函数调用的是unsafe.compareAndSwapInt方法。Unsafe类提供了硬件级别的原子操作,这个原子性的保证并不是靠java本身保证,而是靠一个更底层的与操作系统相关的特性实现(CPU锁),因此,JVM中的CAS的原子性是处理器保障的。

    • CAS存在着ABA的问题:

      • 因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。

      • 解决的办法就是引入版本号,每修改一次则版本号加1,只有版本号和值都和预期值一直,compareAndSet函数才返回true。

JDK1.5提供的原子类原理及使用相关推荐

  1. JDK提供的原子类原理与使用

    原子更新基本类型 原子更新数组 原子更新抽象类型 原子更新字段 原子更新基本类型: package com.roocon.thread.t8; import java.util.concurrent. ...

  2. 高并发编程-07-JDK提供的原子类操作及原理

    1,原子类介绍: 针对数据类型的操作,JDK提供的原子类来方便我们的线程安全控制. 所有的类保存在 java.util.concurrent.atomic 包中 基本数据类型 AtomicIntege ...

  3. cas无法使用_【漫画】CAS原理分析!无锁原子类也能解决并发问题!

    本文来源于微信公众号[胖滚猪学编程].转载请注明出处 在漫画并发编程系统博文中,我们讲了N篇关于锁的知识,确实,锁是解决并发问题的万能钥匙,可是并发问题只有锁能解决吗?今天要出场一个大BOSS:CAS ...

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

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

  5. 16.Atomic原子类体系概览

    老王:小陈啊,从今天开始我们就要进入Atomic原子类系列的学习了,首先啊给你看一下JDK中提供给我们使用的原子类有哪些? 小陈:好啊,我记得JUC下面提供的原子类还是挺多的,所有的原子类的使用和底层 ...

  6. 6.Atomic原子类

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

  7. Java中常用的原子类

    文章目录 一.什么是原子类 二.原子类的底层实现 三.常用的原子类 3.1.AtomicInteger与AtomicLong 3.2.LongAdder 四.原子类的性能测试 4.1.测试程序 4.2 ...

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

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

  9. Atomic原子类及原理

    目录 1 前言 2 unsafe类对Atomic原子类的支持 3 AtomicInteger的内部实现 3.1 准备 3.2 读 3.3 写 4 CAS机制 4.1 基本操作数 4.2 例子 4.3 ...

最新文章

  1. 青龙羊毛——东方头条(搬砖,非原创)
  2. .net 使用 Aspose.Words 进行 Word替换操作
  3. 移动端geolocation插件+百度地图js获取地址
  4. 重装系统,配置CVS
  5. Java SSM框架之MyBatis3(六)MyBatis之参数传递
  6. Asp.net报错汇总:回发或回调参数无效
  7. linux下安装mysql-5.7.20
  8. Oracle系列--基础理论
  9. Keras的基本使用(1)--创建,编译,训练模型
  10. asp.net大型制造业进销存源码 c#源代码 bs 本系统 为ASP.NET C# WinForm源码,数据库为SQL Server。系统完全开源,
  11. php中的oop思想,oop_php oop思想_oop和ood
  12. P58-前端基础HTML-表格入门介绍
  13. java.sql.SQLException: The server time zone value ‘�й���׼ʱ��‘ is unrecognized or represents more tha
  14. 【20191025】考试
  15. 3D打印显神威:世界首颗3D打印卫星将入轨
  16. 【Pytorch】torch.nn.Conv1d()理解与使用
  17. java实现请求排队处理_【高并发】秒杀系统高并发请求排队处理
  18. 物料主数据 分类视图导入 BAPI_OBJCL_CREATE
  19. linux swap分区满了,Linux下如何释放内存、swap分区满了怎么办!
  20. Protel99 画层次原理图、多Part元件的绘制方法的一些心得记录

热门文章

  1. 自定义灵活 自动滚动的Dialog
  2. Generator 实现
  3. go 访问数据库mysql基础
  4. python tk text 自动换行_Python tkinter之Text
  5. MFC小笔记:TabCtrl父子窗口传递消息
  6. spark分区连接mysql_Spark数据存储和分区操作
  7. 【java】ssh the connection is not authenticated
  8. 【Spark】Spark的一个案例 Encountered removing nulls from dataset or using handleInvalid = “keep“ or “skip“
  9. 【ElasticSearch】Es 源码之 IndicesModule 源码解读
  10. 95-40-115-java.util.concurrent-线程-AbstractExecutorService