zoukankan      html  css  js  c++  java
  • Volatile

    Volatile

    Volatile 是 Java 虚拟机提供轻量级的同步机制
    1、保证可见性
    2、不保证原子性
    3、禁止指令重排

    1、保证可见性

    public class JMMDemo {
        // 不加 volatile 程序就会死循环!
        // 加 volatile 可以保证可见性
        private volatile static int num = 0;
    
        public static void main(String[] args) { // main
    
            new Thread(()->{ // 线程 1 对主内存的变化不知道的
                while (num==0){
    
                }
            }).start();
    
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            num = 1;
            System.out.println(num);
    
        }
    }
    

    2、不保证原子性

    // volatile 不保证原子性
    public class VDemo02 {
    
        // volatile 不保证原子性
        private volatile static int num;
    
        public static void add(){
            num++; // 不是一个原子性操作
        }
    
        public static void main(String[] args) {
    
            //理论上num结果应该为 2 万
            for (int i = 1; i <= 20; i++) {
                new Thread(()->{
                    for (int j = 0; j < 1000 ; j++) {
                        add();
                    }
                }).start();
            }
    
            while (Thread.activeCount()>2){ // main  gc
                Thread.yield();
            }
    
            System.out.println(Thread.currentThread().getName() + " " + num);
    
    
        }
    }
    

    volatile怎么用

    volatile只保证可见性,而加锁机制既可以确保可见性又可以确保原子性。当且仅当满足以下条件下,才应该使用volatile变量:

    1、对变量的写入操作不依赖变量的当前值,或者确保只有单个线程变更变量的值。
    2、该变量不会于其他状态一起纳入不变性条件中
    3、在访问变量的时候不需要加锁。

    volatile怎样实现原子性(不使用Lock和synchronized)

    使用原子性包装类

    image-20200804221042982

    // volatile 不保证原子性
    public class VDemo02 {
    
        // volatile 不保证原子性
        // 原子类的 Integer
        private volatile static AtomicInteger num = new AtomicInteger();
    
        public static void add(){
    //         num++; // 不是一个原子性操作
            num.getAndIncrement(); // AtomicInteger + 1 方法, CAS
        }
    
        public static void main(String[] args) {
    
            //理论上num结果应该为 2 万
            for (int i = 1; i <= 20; i++) {
                new Thread(()->{
                    for (int j = 0; j < 1000 ; j++) {
                        add();
                    }
                }).start();
            }
    
            while (Thread.activeCount()>2){ // main  gc
                Thread.yield();
            }
    
            System.out.println(Thread.currentThread().getName() + " " + num);
    
    
        }
    }
    

    这些类的底层都直接和操作系统挂钩!在内存中修改值!Unsafe类是一个很特殊的存在!

    3、禁止指令重排

    什么是 指令重排:你写的程序,计算机并不是按照你写的那样去执行的。
    源代码-->编译器优化的重排--> 指令并行也可能会重排--> 内存系统也会重排---> 执行
    处理器在进行指令重排的时候,考虑:数据之间的依赖性!

    int x = 1; // 1
    int y = 2; // 2
    x = x + 5; // 3
    y = x * x; // 4
    我们所期望的:1234 但是可能执行的时候回变成 2134 1324 不可能是 4123!
    

    可能造成影响的结果: a b x y 这四个值默认都是 0;

    线程A 线程B
    x=a y=b
    b=1 a=2

    正常的结果: x = 0;y = 0;但是可能由于指令重排

    线程A 线程B
    b=1 a=2
    x=a y=b

    volatile怎样实现的禁止指令重排?

    volatile可以避免指令重排: 利用内存屏障。
    内存屏障作用:
    1、保证特定的操作的执行顺序!
    2、可以保证某些变量的内存可见性 (利用这些特性volatile实现了可见性)

    image-20200804223413678

    Volatile 是可以保持 可见性。不能保证原子性,由于内存屏障,可以保证避免指令重排的现象产生!

    视频参考https://www.bilibili.com/video/BV1B7411L7tE
    上一篇:JMM(Java内存模型)
    下一篇:彻底玩转单例模式

  • 相关阅读:
    重构之路第一篇——重新组织函数的几种方法
    Maven设置http代理
    maven本地仓库路径和修改
    Maven安装目录分析
    使用Python解析Loadrunner的post的中文数据
    Python3 批量创建文件夹
    tomcat设置上传文件大小
    Jenkins启动
    Sublime Text 3 编译Python3
    常用SQL
  • 原文地址:https://www.cnblogs.com/junlinsky/p/13443360.html
Copyright © 2011-2022 走看看