美文网首页
详解volatile关键字和原子引用

详解volatile关键字和原子引用

作者: Liusy01 | 来源:发表于2020-09-15 22:49 被阅读0次

    本篇看一下Volatile关键字和原子引用。

    image.png

    上图就是JUC包结构,总共分成三块

    (1)java.util.concurrent:并发包基础类,包括阻塞队列,线程池相关类,线程安全Map等。

    (2)java.util.concurrent.atomic:原子引用相关类

    (3)java.util.concurrent.locks:线程锁相关类

    线程池技术在之前的讲解应该很清楚了,今天主要解析一个volatile关键字以及原子引用的相关类。这一篇文件涉及到JMM(java内存模型)的知识,之前也有讲解。

    image.png

    一、volatile

    这个关键字,是JVM提供的轻量级的同步机制。可能在业务功能开发中很少涉及,但其是JUC包里面拥有重要地位的大哥。

    首先看下理论:

    (1)保证变量内存可见性:就是当前线程对某个volatile修饰的变量进行操作的话会及时通知到其它线程。比如现在的在线编辑文档,当某个人编辑了文档并保存之后,其他人会立马收到文档已被修改的通知。

    (2)***不保证原子性: ***原子性的意思就是某个元素已经是最小的了,不可在被分割,比如现实中原子就是最小的粒子了,不可能再被分割。而映射到这边就是说某个操作不可在被分割成两个或多个操作,要么成功,要么失败。

    (3)禁止指令重排:JVM为了获取最快的运行速度,会在底层一些没有先后性要求的指令进行一些指令重排的操作,在代码层面是一步一步执行,但在底层执行的时候并不是如此。比如JVM创建了一个对象有三步,创建实例对象、分配空间、将引用指向内存空间,但是第一步和第二步并没有什么关系,这个时候就可能会发生指令重排的操作。

    代码示例:

    (1)可见性代码验证:

    当资源类变量a未加volatile关键字修饰的时候

    详解volatile关键字和原子引用

    当添加了volatile关键字修饰后:

    详解volatile关键字和原子引用

    (2)不保证原子性验证:

    详解volatile关键字和原子引用

    如果保证原子性,那么最后输出的结果应该是20000,而真实输出是19734,所以这可以 证明其不保证原子性。

    JVM为了提供了可保证原子性的原子类。

    二、原子类

    下图就是原子类的结构图

    详解volatile关键字和原子引用

    先用AtomicInteger类是否可以保证原子性。不多说,上图片。

    详解volatile关键字和原子引用

    可以看到,用原子类AtomicInteger类是可以获取期望值的,也就是保证了原子性。

    接下来看一下原子类底层是怎么样的。

    详解volatile关键字和原子引用

    上图可以看到,有三个变量,分别是value,valueOffset,unsafe。

    (1)value:是volatile关键字修饰的,也就是说原子类底层也是volatile变量。

    (2)valueOffset:当前变量的内存位移值。

    (3)unsafe:下图可以看到,unsafe类里面都是native修饰的本地方法,是直接调用其他语言进行操作变量的。

    详解volatile关键字和原子引用

    看一下原子类的addAndGet方法为什么能实现原子性呢?

    点进去会看到它调用了unsafe的getAndAddInt方法。

    详解volatile关键字和原子引用

    继续点下去会看到如下方法,里面有个compareAndSwap。

    详解volatile关键字和原子引用 详解volatile关键字和原子引用

    介绍一个compareAndSwap方法,简称CAS(比较并交换),其有三个值,内存值V,旧的预估值A,更新值B。只有当V和A相等的时候,才会将变量的的值修改为B,否则什么都不干。

    可以看到,上述的getAndAddInt方法是个自旋锁的实现,一直调用getIntVolatile(也是本地方法)方法获取对象的最新值,获取之后就会调用CAS方法。

    CAS:原称为Compare-And-Swap,比较并交换,是一条cpu的并发原语,它的功能是判断内存某个位置的值是否为预期值,如果是则更改为预期值,这个过程是原子的。

    并发原语体现在调用unsafe的方法。原语是属于操作系统的范畴,原语的执行必须是联系的,也就是原子性,不会造成所谓的数据不一致的问题。(这个我也不是很懂,毕竟我不是科班出身的程序员,应该是要学操作系统才会明白)

    (如果大厂面试的时候让你手写一个自旋锁,就把上述代码糊他脸上)

    上述就是其保证原子性的方法,利用自旋锁和unsafe类。

    说完原子类的底层,进行拓展一下:上述可以看到,原子类java只提供了基本类型的封装,例如AtomicInteger,AtomicLong等,但是工作中需要很多其他自定义的实体,也要保证原子性,该怎么办呢?

    别慌,JVM还提供了原子引用类型AtomicReference,下图可见此类是一个泛型类。

    详解volatile关键字和原子引用

    下次会带大家看一下CAS导致的ABA问题。

    =======================================================

    我是Liusy,一个喜欢健身的程序员。

    欢迎关注【Liusy01】,一起交流Java技术及健身,获取更多干货。

    相关文章

      网友评论

          本文标题:详解volatile关键字和原子引用

          本文链接:https://www.haomeiwen.com/subject/sipqyktx.html