美文网首页
java多线程并发技术之Volatile

java多线程并发技术之Volatile

作者: Jowney | 来源:发表于2019-08-15 18:07 被阅读0次

前言:

这个volatile真的是折磨我了很久,因为对于Android开发来说确实不是很重要,但是面试啊什么的会经常遇到。上网搜一下全是什么可见性、原子性然后搞几个无关痛痒的例子也说不到点上,看完以后感觉懂了,但是仔细琢磨一下还是有逻辑漏洞。经过长时间查资料和思考后决定写下自己的理解。

一、知识点(可以先看样例再看这个)

  1. Volatile中文意思是“不稳定的”,它只能用来修饰变量。那当它来修饰变量时不就是说明这个变量是不稳定的吗,妙!
  1. 线程内存管理模型(这个很简单但是很重要哦!)

    图片来自《java多线程编程核心技术》
    一句话总结:每个线程所占用的内存都是相互独立的,并且与主内存也是相互独立的。(只总结了一下重点,具体细节自己去挖掘去体验)哦!原来是这样,在线程想要修改主内存中某个变量的值(比如某个全局变量)原来要经过至少三步,第一步读取数据到自己的工作内存,第二步修改,第三步写入主内存。
  2. 可见性是当一个线程修改了共享变量时,另一个线程可以读取到这个修改后的值(注意线程中操作共享变量的步骤),我们平时见到的大多都有可见性,那举一个不可见性的例子,下面的例子是由于不可见性导致的死循环。

  3. Volatile关键字的作用是保证了变量在线程的可见性,它提示线程每次从共享内存中读取变量,而不是从私有内存中读取,这样就保证了数据的可见性。

二、样例分析

public class MyClass {
    private  static boolean mRunning = true;
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                int i = 0;
                while(MyClass.mRunning){
                    i++;

                   /* for(int k=0;k<100000;k++){
                        new Object();
                    }*/
                    
                }
                System.out.println("程序退出");
            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                mRunning = false;  //设置mRunning为false,使上面的线程结束while循环
            }
        }).start();
    }
}

这段代码应该很好理解吧!第一个线程运行以后在while中执行i++,然后暂停一秒执行第二个线程,第二个线程将标志位设为false,让第一个线程结束循环。
按照正常逻辑程序运行后应该会打印“程序退出”四个大字对哇!但是人世间往往事与愿违,运行以后发现程序根本就不会停止。但是取消掉代码中的注释后发现程序可以正常停止了,更让人疑惑的是注释的代码根本没有任何意义啊!,那么就有了两个疑惑(^ _ ^)

  • Q1:为什么注释代码后程序不会终止?
    A1:因为 boolean mRunning=true 的变量值被前面线程(简称线程A)加载到自己的工作内存,在后面的线程(简称线程B)改变 boolean mRunning=false 之后不一定会立马写入主存(不过这道题中应该会马上写入主存,因为线程执行完 is=false之后线程就要退出了),即便立马写入了主存后线程A也不一定马上load到工作内存中,所以程序一直不会终止?这个是我们大多数人想到的,但其实JVM针对现在的硬件水平已经做了很大程度的优化,基本上很大程度的保障了工作内存和主内存的及时同步,相当于默认使用了volatile。但只是最大程度!在CPU资源一直被占用的时候,工作内存与主内存中间的同步,也就是变量的可见性就会不那么及时!
  • Q2:为什么取消注释后程序就可以终止了?
    A2:因为我们知道当CPU在被占用的时候,数据的可见性得不到很好的保证。就像上面的例子中,没有添加代码之前,程序会一直循环做i++操作,所以CPU会被运算占用;而对于大量的new Object()操作来说,CPU已经不是主要站时间的操作,真正的耗时应该在内存的分配上(因为CPU的处理速度明显快过内存,不然也不会有CPU的寄存器了),所以CPU空闲后会遵循jvm优化基准,尽可能快的保证数据的可见性,从而从主存将mRunning变量同步到工作内存中,最终导致程序的结束;
  • Q3:说了这么多貌似和Volatile没什么关系啊
    A3:怎么可能没有关系,如样例中的代码,现在用Volatile修饰mRunning变量,运行代码后发现代码可以正常停止,这说明Volatile解决了一个问题,也就是不管CPU有多忙,当线程中读取Volatile修饰的变量时都会保证该变量的可见性,也就是会刷新工作内存中变量的数据,使得自己工作内存中的数据与共享内存中数据保持一致,所以就不会出现Q1问题中描述的现象,CPU太忙导致来不及更新工作内存中的数据。

参考来源:
1.并发编程网
2.趣谈Java变量的可见性问题
3.《java多线程编程核心技术》

相关文章

网友评论

      本文标题:java多线程并发技术之Volatile

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