• Java并发编程之volatile关键字


    简介

    volatile关键字主要是用来解决共享变量内存可见性问题CPU指令乱序执行问题

    下面通过一个实例来说明下这两个问题导致的原因和volatile如何解决这两个问题。

    volatile的使用

    public class TaskRunner {
    
        private static int number;
        private static boolean ready;
    
        private static class Reader extends Thread {
    
            @Override
            public void run() {
                while (!ready) {
                    Thread.yield();
                }
    
                System.out.println(number);
            }
        }
    
        public static void main(String[] args) {
            new Reader().start();
            number = 42;
            ready = true;
        }
    }
    

    上面程序的执行结果除了延迟一段时间正常输出42之外,还可能存在以下两种结果:

    1.线程永远不会停止

    这种情况属于前面说的共享变量内存可见性问题。可见性是指当一个线程修改了某一个共享变量的值时,其他线程是否能够立即知道这个修改。

    如下Java内存模型的抽象图所示,线程之间的共享变量都存储在主内存中,但每个线程本地内存都会存有共享变量的副本。假设前面例子中主线程和子线程分别在不同的CPU里,主线程将ready的值设置为true,这个值并不会立刻同步到主内存,所以子线程也就不能读取到最新的值,而读取的是本地内存变量副本的值。

    2.线程结束,输出结果number值为0

    这种情况属于前面说的CPU指令乱序执行问题。CPU的速度至少比内存快100倍,为了提升效率,会打乱原来的执行顺序,会在一条指令执行过程中,去同时执行另一条指令,当然乱序执行的前提是两条指令没有依赖关系

    在单线程的情况下,重排序能够保证乱序执行结果和顺序执行结果是一致的,但是在多线程的情况下就可能会有问题了。如上可能会出现ready = true先执行,而number = 42则刚好在输出语句后执行。

    使用volatile避免出现上述问题
    public class TaskRunner {
    
        private volatile static int number;
        private volatile static boolean ready;
    
        //...
    }
    

    1.当一个变量被声明volatile时,线程对它的修改会直接刷新到主内存,其他线程对它的读取也是直接从主内存读取,这样就能保证所有线程读取到这个变量的值都是一致的。

    2.volatile能够禁止指令重排序,在被声明volatile变量上面执行的指令不可以乱序。

    volatile和synchronized的比较

    volatile是轻量级的synchronized,它能保证可见性和有序性,但它不能保证原子性。之所以说volatile是轻量级的synchronized,是因为volatile的执行效率要更高,而synchronized是独占锁,会有锁的开销。

    synchronized能够保证可见性,当线程进入synchronized块时,会先清空本地内存中的变量值,然后从主内存中读取最新的值。当线程退出synchronized块时,会将本地内存中的变量值同步到主内存。

    synchronized块可以看作一个整体,同一时间只有一个线程运行synchronized块代码,不会被其他线程干扰,所以也就能保证原子性和有序性。

  • 相关阅读:
    register based 和 stack based虚拟机的区别
    Java in a Nutshell学习笔记
    Java中interface和abstract class的区别和联系
    Java中final的作用
    Android 源码下载
    Android Fragment 你应该知道的一切
    Android Fragment 真正的完全解析(下)
    Android Fragment 真正的完全解析(上)
    IntelliJ IDEA 使用总结
    Linux在目录中查找某个函数
  • 原文地址:https://www.cnblogs.com/seve/p/14520125.html
Copyright © 2020-2023  润新知