• voliate实战:voliate可见性验证&有序性&非原子性验证


    一、可见性验证

    下面的程序验证了voliate的可见性。

    public class VolatileVisibilityTest {
    
        private static volatile boolean inintFlag = false;
    
        public static void main(String[] args) throws InterruptedException {
            new Thread(() -> {
                System.out.println("waiting data...");
                while (!inintFlag){
    
                }
                System.out.println("===============success");
            }).start();
    
            Thread.sleep(2000);
    
            new Thread(() -> prepareData()).start();
        }
    
        public static void prepareData(){
            System.out.println("prepare data.....");
            inintFlag = true;
            System.out.println("prepare data end....");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    代码执行过程如下:
    在这里插入图片描述

    在这里插入图片描述

    • voliate可见性的底层实现原理
      通过程序执行的汇编指令发现,是通过锁机制实现的。

    验证过程

    • 下载反汇编程序插件
      下载地址:https://download.csdn.net/download/luckywuxn/88347740
    • 插件配置
      将hsdis-amd64.dll放在 $JAVA_HOME/jre/bin/server 目录下
    • idea中设置启动参数
      -server -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=compileonly,*VolitaleDemo.main
    • 启动程序查看结果
      控制台搜索lock关键字,将看到如下结果,结果现在lock指令执行的程序是在源代码的第28行。
      在这里插入图片描述

    二、有序性

    • 示例一
    public class VolatileSerialTest {
    
        static int x = 0,y = 0;
        static int a = 0,b = 0;
    
        public static void main(String[] args) throws InterruptedException {
            long startTime = System.currentTimeMillis();
            for (int i = 0;; i ++){
                x = 0;
                y = 0;
                a = 0;
                b = 0;
                Thread one = new Thread(() -> {
                    a = y;
                    x = 1;
                });
                Thread other = new Thread(() -> {
                    b = x;
                    y = 1;
                });
                one.start();
                other.start();
                one.join();
                other.join();
                if (a == 1 && b == 1){
                    long endTime = System.currentTimeMillis();
                    System.out.println("经过" + (endTime - startTime) + "ms," + i + "次之后a=b=1");
                    break;
                }
                System.out.println("当前执行" + i + "次");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    运行上面代码,得到以下结果,由此我们可以得到结论,在没有使用voliate关键字时,两个线程中的两条指令是可以重排序的。
    在这里插入图片描述

    • 示例二
    public class VolatileSerialTest2 {
        private static VolatileSerialTest2 instance = null;
    
        private VolatileSerialTest2(){
        }
    
        public static VolatileSerialTest2 getInstance(){
            if (instance == null){
                synchronized (VolatileSerialTest2.class){
                    if (instance == null){
                        instance = new VolatileSerialTest2();
                    }
                }
            }
            return instance;
        }
    
        public static void main(String[] args) {
            VolatileSerialTest2 instance = VolatileSerialTest2.getInstance();
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    编译之后,通过idea插件jclasslib插件可以看到字节码文件如下
    在这里插入图片描述
    由JMM规范可知,如果第11、12行是最新as-if-serial & happens-before 原则的,所有这两条指令是可能重排序的,为了防止重排序,我们只需要加上voliate关键字就可以了。

    三、原子性验证

    下面是一个原子性验证的代码:

    class Counter {
        private volatile int count = 0;
        public void increment() {
            count++;
        }
        public int getCount() {
            return count;
        }
    }
    
    public class VolatileAtomicTest {
    
        public static void main(String[] args) {
            final Counter counter = new Counter();
            // 创建两个线程,同时递增计数器的值
            Thread thread1 = new Thread(() -> {
                for (int i = 0; i < 1000; i++) {
                    counter.increment();
                }
            });
            Thread thread2 = new Thread(() -> {
                for (int i = 0; i < 1000; i++) {
                    counter.increment();
                }
            });
            thread1.start();
            thread2.start();
            // 等待两个线程执行完成
            try {
                thread1.join();
                thread2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 输出最终的计数器值
            System.out.println("Final Count: " + counter.getCount());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    运行上面代码,结果如下
    在这里插入图片描述
    上面程序使用两个线程同时对voliate修饰的变量count进行累计操作,voliate对所有线程都是可见的,那为什么结果不是2000呢,这是由于voliate修饰的变量并不是原子的。

    最后,给大家展示一下voliate的五层实现。
    在这里插入图片描述

  • 相关阅读:
    hive中get_json_object获取多个字典中同一个key的value
    vue+python把woff字体文件中的字体全部读取出来
    C语言 深度探究C语言中的预处理器
    C语言暑假学习刷题——Day8
    wx小程序学习笔记day01
    Linux——ip协议
    SQL SELECT 子查询与正则表达式
    数据结构 优先级队列(堆)
    使用pytorch实现深度可分离卷积改进模型的实战实践
    mysql查看连接池的命令
  • 原文地址:https://blog.csdn.net/luckywuxn/article/details/132925148