volition到CAS到ABA

1:谈谈你对volatile的理解

volatile三大特性

volatile是Java虚拟机提供的轻量级的同步机制,有三大特性:保证可见性、不保证原子性、禁止指令重排。

volatile int num=0;
什么是可见性(由JMM引)?

JMM(Java内存模型),本身是一种抽象的概念并不真实存在,它描述的是一种规范或者规则,通过这组规范定义了程序中的各个变量的访问方式。

JMM要满足可见性、原子性、有序性。满足这三个即线程安全

JMM关于同步的规定:

  • 线程解锁前,必须把共享变量的值刷新回主内存
  • 线程加锁前,必须读取主内存的最新值到自己的工作内存
  • 加锁解锁是同一把锁

可见性?

​ Java规定所有变量都存在主内存当中,主内存是共享内存区域,线程对变量的操作只能在工作内存中操作,有三个线程想取出来主内存中的一个东西,第一步先将变量从主内存中拷贝到自己的工作空间,然后对变量进行操作,操作完成后再将变量写回主内存。而线程之间的通讯(传值)是需要通过主内存来进行的,不同的线程无法访问彼此的工作内存。

如果,三个线程同时取出,而线程1将变量更改了放了回去,要让其余线程知道其已经被更改。这就是可见性

什么是不保证原子性?

原子性指的是?

​ 不可分割、完整性、某个线程正在做某个业务时,中间不可以被加塞或者是分割,需要整体完成。

不保证原子性就会出现写丢失的现象

不保证原子性怎么理解?
假如三个线程,线程1、2是给值加1(加1的那个用volatile修饰了),那么运行后,主内存中的值应该是2,由于多线程竞争的调度关系,某一时间段,线程1、2从主内存获取到值都是0,各自在各自的工作空间加1后,往主内存写的时候,将会出现某一时间段1号线程刚要写这个1的时候被挂起了,线程2将1写入了进去,这时volatile通知别的线程,在没有反应过来的情况下,线程1将自己的那个1写入,将这个1覆盖了。

如何解决:

​ 第一:添加synchronized关键字,不过这样有点杀鸡用牛刀

​ 第二:使用juc下的AtomicInteger(接CAS)

什么是禁止指令重排?

执行源代码流程:
源代码–编译器优化的重排–指令并行的重排–内存系统的重排–最终执行

volatile实现了禁止指令重排,从而避免多线程环境下程序出现乱序执行的现象

Synchronized关键字

用来加锁

在哪些地方用到过volatile?

单例模式DCL代码(双端检索)机制不一定线程安全,原因是底层存在指令重排的可能,需要volatile可以禁止指令重排

2:CAS你知道吗?

CAS是什么?

CAS是一条CPU并发原语,功能是判断内存某个位置的值是否为预期值,如果是则改为新的值,这个过程是原子性的。(比较并交换

假如在主物理内存存在一个5,有两个线程,线程1、2首先获取到主物理内存中的5,再带着获取到的这个值之后和主物理内存中的值进行对比,如果成功,则给主物理内存返回线程所修改的值。线程1,获取到的值和主物理内存中的值进行对比,一样后,就给主物理内存返回一个2019。线程2,获取到的值和主物理内存中的值对比,对比不上(5不等于2019),所以修改不了。这个对比修改就是CAS。

//代码示例
public static void main(String[] args){
    AtomicInteger atomicInteger = new AtomicInteger(5);
    System.out.println(atomicInteger.compareAndSet(5,2019)+atomicInteger.get());
    System.out.println(atomicInteger.compareAndSet(5,2099)+atomicInteger.get());
}
//输出结果
true  2019
false 2019
CAS底层原理,谈谈你对UnSafe的理解?

CAS的底层思想:

​ CAS就是比较当前工作内存中的值和主内存中的值,如果相同就执行规定操作,否则继续比较直到主内存和工作内存中的值一致为止

CAS有3个操作数,内存值V,旧的预期值A,要修改的更新值B

当且仅当预期值A与内存值V相同时,将内存值V修改为B,否则什么都不做,在进行自旋的比较,直到成功为止

UnSafe类:

​ 之所以原子整型下不用添加Synchronized关键字依然保证原子性,就是因为UnSafe类。是CAS的核心类,相当于一个后门,基于该类可以直接操作特定内存的数据,Java中的CAS操作的执行依赖于UnSafe类的方法。

CAS缺点?

循环时间长,开销大

只能保证一个共享变量的原子操作(对于多个的,那就只能通过加锁了)

引出来ABA问题

3:原子类AtomicInteger的ABA问题谈谈?原子更新引用知道吗?

ABA是什么?

​ 有两个线程,线程1的执行时间是10秒,线程2的执行时间是2秒。线程1、2同时从主物理内存中取出来一个2放到自己的工作内存,线程2两秒后传入一个200,后线程2再过两秒,又将200改为2。就这样,线程1执行的时候,工作空间和主物理内存中的值虽然也是一样的,也可以执行。但这10秒中,那个值换了很多次。

​ 尽管线程1操作时成功的,但不代表过程没有问题

AtomicReference原子引用

​ 根据下面代码解释。刚开始其中的值是z3和22岁。但通过cas之后,其内的值就变成了li4和25岁。

这就是原子引用,先定义一个类,其内参数定义好。就比如,当其内是z3的时候,不但将z3换成li4,还将年龄改变。

class User{
    String userName;
    int age;
}
public class AtomicReferenceDemo{
  public static void main(String[] args){
     User z3=new User(z3,22);
     User li4=new User(li4,25);
     
     AtomicReference<User> atomicReference=new AtomicReference<>();
     atomicReference.set(z3);  //这就是说明其内的初始值是z3和22岁
      System.out.println(atomicReference.compareAndSet(z3,li4)+atomicReference.get.toString()); 
  }
}
AtomicStampedReference版本号原子引用

新增一种机制(版本号),类似于乐观锁

线程1、2从主物理内存中取东西时,加上版本号,每次修改主物理内存中的值时,不但要对比值是否一样,还要对比版本号是否相同。修改一次,版本号会加1,这样如果发生了ABA问题,即使其中的值一样,但版本号不一样。这样可以有效解决ABA问题。

解决ABA问题

加入版本号即可

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值