1)解决并发不是用锁就能解决吗,那SDK干嘛还要搞个原子类出来?

  • 锁虽然能解决,但是加锁解锁始终还是对性能是有影响的,并且使用不当可能会造成死锁之类的问题。

2)原子类是怎样使用的,比如说我要实现一个线程安全的累加器?

 public class Test {
   AtomicLong count =
     new AtomicLong(0); // 原子类变量count
   void add10K() {
     int idx = 0;
     while(idx++ < 10000) {
       count.getAndIncrement();// 原子操作
    }
  }
 }

3)原子类是无锁的,那他底层是靠什么来实现原子安全的?

  • 靠硬件。我们的CPU为了解决并发问题,提供了CAS指令,而CPU的指令本身就是原子性的。

4)什么是CAS呢?

  • compare and swap ,他主要就看3个参数,A是共享变量的内存地址,B是用于和原地址值比较的,C是我们要更新的值。

  • 其实就是把原来的共享变量的值取出一份来,然后你要更新的话,得对比一下,当前的值和我取出来保存的这份值是不是相同的,如果是相同的,那就可以修改,不同的话说明被别人修改过了,那你现在就不能更新。

     ​
     class SimulatedCAS{
       int count;
       synchronized int cas(
         int expect, int newValue){
         // 读目前count的值
         int curValue = count;
         // 比较目前count值是否==期望值
         if(curValue == expect){
           // 如果是,则更新count的值
           count = newValue;
        }
         // 返回写入前的值
         return curValue;
      }
     }

5)假如我当前的值和我取出来的那份值不一样了,那该怎么办?

  • CAS一般带有自旋,所谓自旋也就是循环的意思。当值不同了 ,那就从头来进行:取值来放着--->对比--->相同的话那就更新,不同就从头再来。

     ​
     class SimulatedCAS{
       volatile int count;
       // 实现count+=1
       addOne(){
         do {
           newValue = count+1; //①
        }while(count !=
           cas(count,newValue) //②
      }
       // 模拟实现CAS,仅用来帮助理解
       synchronized int cas(
         int expect, int newValue){
         // 读目前count的值
         int curValue = count;
         // 比较目前count值是否==期望值
         if(curValue == expect){
           // 如果是,则更新count的值
           count= newValue;
        }
         // 返回写入前的值
         return curValue;
      }
     }
  • 从上面代码我们也可以看出来,完全是没有加锁解锁的操作的,所以CAS这种无锁实现并发的操作性能很好。

6)我们说凡事都有两面性,CAS他就没任何的缺点吗?

  • 会存在ABA问题,比如之前我取出来了一份值是A,但是在我进行对比之前,其它线程悄悄滴过来 ,把我的共享变量修改为了B,然后又修改成了A。虽然看到的都是A,其实这是被修改过的了。

7)两个数值反正都是相同的,不影响我的更新,那我还在乎ABA干嘛?

  • 如果我们只是进行数值的原子递增之类的操作,那我们是不需要关心的。但是如果是对象呢,对象就比数值讲究多了,可能 A表面都是 一样的,但是属性是不一样的。

8)怎样解决ABA问题呢?

  • 在使用 CAS 方案的时候,一定要先 check 一下。

9)前面我们使用原子类 AtomicLong 的 getAndIncrement() 方法替代了count += 1,从而实现了线程安全。原子类 AtomicLong 的 getAndIncrement() 方法内部就是基于 CAS 实现的,那 Java 是如何使用 CAS 来实现原子化的count += 1的?

  • Java 1.8 版本中,getAndIncrement() 方法会转调 unsafe.getAndAddLong() 方法。这里 this 和 valueOffset 两个参数可以唯一确定共享变量的内存地址。

     final long getAndIncrement() {
       return unsafe.getAndAddLong(
         this, valueOffset, 1L);
     }

10)unsafe.getAndAddLong() 方法的底层源码实现是怎样的?

  • 该方法首先会在内存中读取共享变量的值

  • 之后循环调用 compareAndSwapLong() 方法来尝试设置共享变量的值,直到成功为止。

  • compareAndSwapLong() 是一个 native 方法,只有当内存中共享变量的值等于 expected 时,才会将共享变量的值更新为 x,并且返回 true;否则返回 fasle。

     ​
     public final long getAndAddLong(
       Object o, long offset, long delta){
       long v;
       do {
         // 读取内存中的值
         v = getLongVolatile(o, offset);
      } while (!compareAndSwapLong(
           o, offset, v, v + delta));
       return v;
     }
     //原子性地将变量更新为x
     //条件是内存中的值等于expected
     //更新成功则返回true
     native boolean compareAndSwapLong(
       Object o, long offset,
       long expected,
       long x);

11)SDK中提供了哪些原子类给我们使用?

使用提示:Java 提供的原子类能够解决一些简单的原子性问题,但是我们所有原子类的方法都是针对一个共享变量的,如果你需要解决多个变量的原子性问题,建议还是使用互斥锁方案。原子类虽好,但使用要非常小心。

java的原子类到底是啥?ABA,CAS又是些什么?的更多相关文章

  1. java 线程 原子类相关操作演示样例 thinking in java4 文件夹21.3.4

    java 线程  原子类相关操作演示样例 package org.rui.thread.volatiles; import java.util.Timer; import java.util.Time ...

  2. Java并发—原子类,java.util.concurrent.atomic包(转载)

    原子类 Java从JDK 1.5开始提供了java.util.concurrent.atomic包(以下简称Atomic包),这个包中 的原子操作类提供了一种用法简单.性能高效.线程安全地更新一个变量 ...

  3. java并发:原子类之AtomicLong

    原子类之AtomicLong java线程中的操作,需要满足原子性.可见性等原则,比如i++这样的操作不具备原子性, A线程读取了i,另一个线程执行i++,A线程再执行i++就会引发线程安全问题 推荐 ...

  4. (转)Java atomic原子类的使用方法和原理(一)

    在讲atomic原子类之前先看一个小例子: public class UseAtomic { public static void main(String[] args) { AtomicIntege ...

  5. java的原子类 AtomicInteger 实现原理是什么?

    采用硬件提供原子操作指令实现的,即CAS.每次调用都会先判断预期的值是否符合,才进行写操作,保证数据安全. CAS机制 CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并替换. ...

  6. Java - “JUC”原子类

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

  7. CAS 算法与 Java 原子类

    乐观锁 一般而言,在并发情况下我们必须通过一定的手段来保证数据的准确性,如果没有做好并发控制,就可能导致脏读.幻读和不可重复度等一系列问题.乐观锁是人们为了应付并发问题而提出的一种思想,具体的实现则有 ...

  8. Java多线程系列——原子类的实现(CAS算法)

    1.什么是CAS? CAS:Compare and Swap,即比较再交换. jdk5增加了并发包java.util.concurrent.*,其下面的类使用CAS算法实现了区别于synchronou ...

  9. java并发编程(十三)----(JUC原子类)引用类型介绍(CAS和ABA的介绍)

    这一节我们将探讨引用类型原子类:AtomicReference, AtomicStampedRerence, AtomicMarkableReference.AtomicReference的使用非常简 ...

随机推荐

  1. LeetCode-095-不同的二叉搜索树 II

    不同的二叉搜索树 II 题目描述:给你一个整数 n ,请你生成并返回所有由 n 个节点组成且节点值从 1 到 n 互不相同的不同 二叉搜索树 .可以按 任意顺序 返回答案. 二叉搜索树(Binary ...

  2. laravel7 百度智能云检测图片是否合规

    APP 文件下建一个Libs目录建一个BaiduService文件,需要检测引入进行调用即可 BaiduService文件内容如下 <?php namespace App\Libs; use A ...

  3. tensorflow源码解析之framework-device

    目录 什么是设备 设备属性描述 device_base 关系图 涉及的文件 迭代记录 1. 什么是设备 "设备"是一个很容易引起混淆的概念,在TF中,设备device专指能够执行实 ...

  4. jq集成月份插件(不要年月日,只要年月)

    最近项目中报表统计需要按照月份进行统计,以前用的jq-ui的插件,里面集成的是年月日,修改了好久,觉得太过麻烦,就换了个思路,由于赶时间,所以就度娘了,找到了一个很简洁又简单的Demo,但是发现和我的 ...

  5. ArcMap问题及解决方案

    1.导出的矢量文件dbf格式用Excel打开后全是乱码怎么解决? 该类问题的部分解决方案是将数据用[表转Execl ] 工具转出来 能根本解决的方法是修改注册表 详细解决方案是: 乱码解决办法:①快捷 ...

  6. 关于C#窗体应用程序的一点总结

    1.在窗体Form在Form_Load函数中写了过多的界面初始化语句导致界面卡顿时 解决方法为:将一些初始化语句写在public Form()函数中,会大大加快程序的初始化加载速度. 2.为butto ...

  7. &取地址运算符

    &可以取得一个变量在内存当中的地址 Register int a; //这种变量不在内存里面,而在CPU里面,是没有地址的, 所以寄存器变量(register)不能用&来取得变量 在计 ...

  8. 关于IIS站点最大并发量分析

    关于IIS站点最大并发量分析,会有如下这个疑问:IIS站点最大并发量是多少? 一般为:   IIS站点最大并发量=队列长度+进程数量[即最大工作进程数] 通过这个公式,可以基本评估出一个IIS站点的最 ...

  9. Charles的安装及设置

    1. Charles的安装 l  下载安装包 l  安装:按照提示信息一步步安装,不做多余解释 l  破解:将Charles.jar包放在安装目录,如D:\Program Files\Charles\ ...

  10. Prometheus自定义监控告警项-3

    prometheus 编写告警规则 将自定义的告警规则写到独立的文件中,prometheus.yml中引用如下: rule_files: - "rules/*.yml" [root ...