首页 > 其他分享 >神奇的volatile

神奇的volatile

时间:2022-09-19 17:37:02浏览次数:67  
标签:instance 线程 内存 jvm volatile 多线程 神奇

什么是volatile?

  打开google,百度一下,你就知道~

  java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量。Java语言提供了volatile,在某些情况下比锁更加方便。如果一个字段被声明成volatile,java线程内存模型确保所有线程看到这个变量的值是一致的。volatile可以保证线程可见性且提供了一定的有序性,但是无法保证原子性。在JVM底层volatile是采用“内存屏障”来实现的。

  说的通俗一点,volatile能帮助我们实现一个重要的特性:保证在多线程处理下线程对资源修改的及时感知;说到volatile就一定会提及JMM内存模型和多线程任务并发场景。

  

  线程中共享的内存,其实并不是全部作为主内存使用的,每个线程对应有自己的堆栈内存,那么我们平时写一个thread,多线程测试会发现,一个共享变量在经过修改后还是会被其他线程所感知,这是为什么呢?

  这里就要说到cpu相关的硬件结构了,最早如果单核cpu单线程,完全不需要考虑这种情况,反正从头到尾都是你自己玩,想玩到天荒地老也不会有人管你。

  现在的服务器标配多核多线程,如果我有一个并发任务去处理,这时候我们并没有办法控制哪个任务会在哪个cpu切片上,具体的cpu如何分配线程,我们以后再说 !

  我们可以看一段代码,这种写法在并发下会有问题:

i = i + 1;i ++

  为什么说这种方式会有并发问题呢,我们知道并发的三个特点:

  1. 原子性:指一个操作是不可中断的,只能一次性执行完,一个操作一旦开始就不 会被其他线程影响
  2. 可见性:如果一个线程修改了资源,其他线程不可见,我们就会说它是存在线程安全问题
  3. 有序性:多线程无法保证顺序,在没有相关依赖关系时,会有指令重排的问题

  在jvm中,Java内存模型只保证了基本读取和赋值是原子性操作,如果要实现更大范围,就需要使用同步块或锁,顾名思义,上面的代码不是原子操作,它其实是将i的值取出来、再操作自增或者增加,再写入,这就是一个比较常见的并发问题:两个线程同时做i++操作,第一个线程先获取到了i的值,给i加1,这时候再写入,但是主内存还并没有将变量的值同步;这时第二个线程来获取i的值(这时候其实获取的是i的脏数据,并没有做完i++动作),它又将i加1后写入;

  针对这种情况,jvm帮我们提供了AtomicReference可以完成自增或者自增数之类的操作,它的底层是由指令实现的,可以避免这种情况,针对AtomicReference我们以后再详细聊聊。

有了volatile,它能帮助我们做什么?

  有了volatile,它最大的特点是,将一个共享变量及时刷新到共享内存中去,使其他线程也可以立即感知到它的变化。

  其实在jvm,字节码文件中,jvm对加了关键字volatile的变量,在变量修改后立即同步主内存并刷新到共享内存中,其他线程可以立即感知

实例
boolean isRun = false;
while(!isRun){
   //doThings
}

isRun = true;


  上面这种自旋唤醒执行的条件,只有当isRun为true时才会执行,上面两步可以分为两个线程执行,如果第二个线程执行了isRun = true的条件,但是主内存并没有同步到共享内存中,线程1是无法感知的,它还会去执行直到主内存同步完成。

  同时,volatile还能够在一定情况下保证有序性:

    volatile可以禁止指令重排,指令重排的意思就是,jvm在优化执行过程时,对没有依赖的相关代码,并不会完全按照代码的顺序执行:

实例
 int i = 0;               
boolean flag = false;
i = 1;                //语句1   
flag = true;          //语句2

  语句1和语句2,我们不能保证肯定是语句1先执行语句2后执行,这就是指令重排的意思,flag和i并没有相互依赖的东西,jvm认为它们重排并不会影响最终结果(思路有点像存储读写分离的保证最终一致性)

摘自//blog.csdn.net/qq_42569136/article/details/123219118
 private static ObjInstance instance;
//线程1
if(instance!=null){
    instance.method();
}
//线程2
if(instance==null){
    synchronized (ObjInstance.class){
        if(instance==null){
            instance = new ObjInstance();
        }
    }
}

  这种情况,就是指令重排后会导致出现执行顺序的问题

我们应该在什么时候使用volatile?

synchronized关键字是防止多个线程同时执行一段代码,那么就会很影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized,但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性

标签:instance,线程,内存,jvm,volatile,多线程,神奇
From: https://www.cnblogs.com/oldEleven/p/16708370.html

相关文章

  • NOIP 2015 神奇的幻方
    #include<bits/stdc++.h>usingnamespacestd;intn,a[40][40],x,y;intmain(){ cin>>n; x=1,y=(n+1)/2; for(inti=1;i<=n*n;i++){ a[x][y]=i; if(!a[(x-2+n)%n......
  • synchronized和volatile区别
    synchronized和volatile区别synochronizd和volatile关键字区别:volatile关键字解决的是变量在多个线程之间的可见性;而sychronized关键字解决的是多个线程之间访问共享资源......
  • 面试java并发~(lock、volatile、cas)
    Lock锁是一个接口,有三个实现类,分别是常用的可重入锁,读锁、写锁。常用的可重入锁,默认一般创建的是非公平锁,就是允许线程插队,而不是按先来后到顺序。非公平锁的目的:是为......
  • 再谈volatile的可见性
    volatile保证可见性的说法有问题,准确说是任何变量都可被访问,只是访问时不一定是最新的值,volatile的作用时,保证线程访问变量时拿到的永远是最新值,所以这个可见性等于保证最......
  • Java并发编程之synchronized 与 volatile
    synchronized同步代码块一般使用Java的synchronized关键字来实现,有两种方式对方法进行加锁操作;第一处,在方法签名处加synchronized关键字;第二,使用synchronized(对象......
  • 神奇结论在哪里
    求证:\[\sum_{i=0}^n\binom{2k}k\binom{2n-2k}{n-k}=4^n\]首先,我们将\(4^n\)视为\(2^{2n}\),赋予其组合意义为长为\(2n\)的\(0/1\)串个数。LHS中组合数的结构指......
  • java 内存模型之 volatile 核心原理与应用
    1.happens-before规则https://blog.csdn.net/qq_39935047/article/details/1203847992.Juc12_Volatile的可见性、不保证可见性、有序性、使用、内存屏障四大指令StoreSt......
  • volatile关键字
    纯笔记文章,仅供自己回忆使用,不具有任何观看价值java内存模型保证可见性cpu总线嗅探机制原子性问题:有线程安全问题,只对单纯赋值具有原子性,解决:使用锁或者原子类禁止指......
  • Volatile介绍
    介绍volatile是Java虚拟机提供的轻量级的同步机制,它可以保证可见性(缓存一致性协议)和有序性(禁止指令重排序,也就是通过内存屏障来实现),但是不保证原子性。JMM介绍JMM......
  • 神奇字体在线小工具
    Alfred插件https://github.com/WangGuibin/alfred-unicode学习React的demohttps://my-react-app-demo.vercel.app/#/alfred基于之前的代码整合了一下,搬了过来~<!......