专栏首页用户7621540的专栏voliate工作实际应用场景

voliate工作实际应用场景

本文源自 公-众-号 IT老哥 的分享

哈喽大家好,我是IT老哥,今天我们来讲讲面试必问的voliate

单线程的情况下呢,我们肯定用不到这个voliate

只有在多线程的情景下才能用到,文章结尾我会举一个经典的案例

voliate三特性

  • 保证可见性;
  • 不保证复合操作的原子性;
  • 禁止指令重排。

第一:可见性

先给大家介绍一下JMM的内存模型

我们定义的共享变量就是存在主内存中,每个线程内的变量是在工作内存中操作的,当一个线程A修改了主内存里的一个共享变量,这个时候线程B是不知道这个值已经修改了,因为线程之间的工作内存是互相不可见的

那么这个时候voliate的作用就是让A、B线程可以互相感知到对方对共享变量的修改,当线程A更新了共享数据,会将数据刷回到主内存中,而线程B每次去读共享数据时去主内存中读取,这样就保证了线程之间的可见性

这种保证内存可见性的机制是:内存屏障(memory barrier)

内存屏障分为两种:Load Barrier 和 Store Barrier即读屏障和写屏障。 内存屏障有两个作用:

1.阻止屏障两侧的指令重排序; 2.强制把写缓冲区/高速缓存中的脏数据等写回主内存,让缓存中相应的数据失效。

这里不深入说这个机制了,对于大家目前的情况可能理解起来比较困难

第二:不保证复合操作的原子性

1、什么叫原子性?

所谓原子性,就是说一个操作不可被分割或加塞,要么全部执行,要么全不执行。

i = 0;            ---1
j = i ;           ---2
i++;              ---3
i = j + 1;        ---4

上面四个操作,有哪个几个是原子操作,那几个不是?如果不是很理解,可能会认为都是原子性操作,其实只有1才是原子操作,其余均不是。

  • 1---在Java中,对基本数据类型的变量和赋值操作都是原子性操作;
  • 2---包含了两个操作:读取i,将i值赋值给j
  • 3---包含了三个操作:读取i值、i + 1 、将+1结果赋值给i;
  • 4---同三一样

Java只保证了基本数据类型的变量和赋值操作才是原子性的(注:在32位的JDK环境下,对64位数据的读取不是原子性操作*,如long、double)

第三:有序性(禁止jvm对代码进行重排序)

有序性:即程序执行的顺序按照代码的先后顺序执行。

一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的,但是不能随意重排序,不是你想怎么排序就怎么排序,它需要满足以下两个条件:

  1. 在单线程环境下不能改变程序运行的结果;
  2. 存在数据依赖关系的不允许重排序

第四:举个非常常见的voliate用法—DCL

什么是DCL呢,其实就是double check lock的简写

DCL很多人都在单利中用过,如下这种写法:

public class Singleton {
   private static Singleton singleton;
   private Singleton(){}

   public static Singleton getInstance(){
       if(singleton == null){                              // 1
           synchronized (Singleton.class){                 // 2
               if(singleton == null){                      // 3
                   singleton = new Singleton();            // 4
               }
           }
       }
       return singleton;
   }
}

表面上这个代码看起来很完美,但是其实有问题

先说一下他完美的一面吧:

1、如果检查第一个singleton不为null,则不需要执行下面的加锁动作,极大提高了程序的性能;

2、如果第一个singleton为null,即使有多个线程同一时间判断,但是由于synchronized的存在,只会有一个线程能够创建对象;

3、当第一个获取锁的线程创建完成后singleton对象后,其他的在第二次判断singleton一定不会为null,则直接返回已经创建好的singleton对象;

但是到底是哪里有错误呢,听老哥细细分析

首先创建一个对象分为三个步骤:

1、分配内存空间

2、初始化对象

3、讲内存空间的地址赋值给对象的引用

但是上面我讲了,jvm可能会对代码进行重排序,所以2和3可能会颠倒,

就会变成 1 —> 3 —> 2的过程,

那么当第一个线程A抢到锁执行初始化对象时,发生了代码重排序,3和2颠倒了,这个时候对象对象还没初始化,但是对象的引用已经不为空了,

所以当第二个线程B遇到第一个if判断时不为空,这个时候就会直接返回对象,但此时A线程还没执行完步骤2(初始化对象)。就会造成线程B其实是拿到一个空的对象。造成空指针问题。

解决方案:

既然上面的问题是由于jvm对代码重排序造成的,那我们禁止重排序不就好了吗?

voliate刚好可以禁止重排序,所以改造后的代码如下:

public class Singleton {
   //通过volatile关键字来确保安全
   private volatile static Singleton singleton;

   private Singleton(){}

   public static Singleton getInstance(){
       if(singleton == null){
           synchronized (Singleton.class){
               if(singleton == null){
                   singleton = new Singleton();
               }
           }
       }
       return singleton;
   }
}

这样就不会存在2和3颠倒的问题了

解决方案二:

基于类初始化

该解决方案的根本就在于:利用classloder的机制来保证初始化instance时只有一个线程。JVM在类初始化阶段会获取一个锁,这个锁可以同步多个线程对同一个类的初始化。

public class Singleton {
   private static class SingletonHolder{
       public static Singleton singleton = new Singleton();
   }

   public static Singleton getInstance(){
       return SingletonHolder.singleton;
   }
}

这种解决方案的实质是:允许步骤2和步骤3重排序,但是不允许其他线程看见。

晚安,兄弟们!

云服务器云硬盘数据库(包括MySQL、Redis、MongoDB、SQL Server),CDN流量包,短信流量包,cos资源包,消息队列ckafka,点播资源包,实时音视频套餐,网站管家(WAF),大禹BGP高防(包含高防包及高防IP),云解析SSL证书,手游安全MTP移动应用安全云直播等等。

给个[在看],是对IT老哥最大的支持

本文分享自微信公众号 - IT老哥(dys_family),作者:IT老哥

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-05-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 最全MySQL性能优化—索引篇

    今天我们来讲讲如何优化MySQL的性能,主要从索引方面优化。下期文章讲讲MySQL慢查询日志,我们是依据慢查询日志来判断哪条SQL语句有问题,然后在进行优化,敬...

    公众号 IT老哥
  • 索引失效原理,终于有人讲明白了

    吊打面试官又来啦,今天我们讲讲MySQL索引为什么会失效,很多文章和培训机构的教程,都只会告诉你,在什么情况下索引会失效。

    公众号 IT老哥
  • zookeeper知识点梳理,面试必看

    zookeeper是一个分布式的,开发源码的分布式应用程序协调服务,它是集群的管理者,监视着集群的各个节点根据节点提交的反馈进行下一步合理的操作

    公众号 IT老哥
  • 【学习】视觉直观感受 7 种常用排序算法

    10月14日发布《统计世界的十大算法》后,很多朋友在后台询问,哪里有“视觉直观感受 7 种常用排序算法”,今天分享给大家,感谢todayx.org。 1. 快速...

    小莹莹
  • 【漫画】为什么说O(n)复杂度的基数排序没有快速排序快?

    老大:我简单给你讲下吧,你学过那么多排序,估计一看就懂了。基数排序,是一种基数“桶”的排序,他的排序思路是这样的:先以个位数的大小来对数据进行排序,接着以十位数...

    帅地
  • 10.1 内部排序

    1、排序(Sorting)时计算机程序设计中的一种重要操作,它的功能是将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列。

    C语言入门到精通
  • 写代码?程序猿?你不能不懂的八大排序算法的Python实现

    信息获取后通常需要进行处理,处理后的信息其目的是便于人们的应用。信息处理方法有多种,通常由数据的排序,查找,插入,删除等操作。本章介绍几种简单的数据排序算法和高...

    风骨散人Chiam
  • 两分钟真能搞懂桶排序

    在数据结构与算法的排序中,我们很多人可能更多的熟悉冒泡排序、快速排序、归并排序。可能对堆排序、桶排序、计数排数等比较生疏,其实这个也没啥复杂的,算法的排序中,我...

    bigsai
  • 漫画:“排序算法” 大总结

    如果原始数组本来已经接近有序,只需要较少的比较交换次数即可完成排序。比如下面这个数组,只有7和8是逆序的:

    小灰
  • 排序算法系列

    概述 概念 排序是计算机内经常进行的一种操作,其目的是将一组“无序”的记录序列调整为“有序”的记录序列。 排序分为内部排序和外部排序。 若整个排序过程不需要访问...

    静默虚空

扫码关注云+社区

领取腾讯云代金券