唐僧喜欢小龙女

导航

volatile 关键字的理解以及使用

1、volatile 关键字的作用

/**
* volatile 关键字的作用
* 1、volatile 是弱化版的synchronized
* 2、保证可见性(多个线程操作同一个变量,不同 的线程能共享变量,一个线程修改了值,另一个线程能看到)
* 3、不保证原子性(原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。)
* 多个线程操作一个变量,其中一个线程操作的时候可能会被别的线程插队。
* 4、禁止指令重排。
* volatile用的最多的地方就是单例模式的双重锁模式里面
*
*
*/

2、volatile 关键字的可见性

代码中的num 如果不用volatile修饰那么循环会一直执行下去的

/**
 *  保证可见性的例子
 */
public class JMMDemo {
    private volatile static int num = 0;
    public static void main(String[] args) { //这是一个线程

        new Thread(()->{  // 如果这里 使用的num不用volatile 修饰,这个线程里面的循环会一直执行
            while (num == 0){}
        },"gao").start();

        try{
            TimeUnit.SECONDS.sleep(2);
        }catch (InterruptedException e){
        }
        num = 1;      //主线程修改值

        System.out.println(num);
    }
}

  

2、volatile 不保证原子性的例子

   /**
     * 一、num++ 是不是一个原子性操作
     *      num++ 不是一个原子性的操作,导致最后的结果比理论的20000 小。如果是20000 那么证明num++ 是原子性的
     * 二、保证测试结果是20000 的解决办法:
     *      1、 加synchronized 锁
     *      2、 用 lock
     *      3、 使用原子类
     * 三、使用volatile 保证不了结果是20000的原因
     *      原因是volatile 保证不了原子性,如果最后的结果是 20000 那么就能说明volatile 能保证原子性
     * 四、num ++ 的底层操作
     *      使用 Javap -c 这个命令对num++ 的编译后的代码进行反编译 num++ 会有好几个操作,导致最后的结果不是20000;
     */

  

 

 

// 不保证原子性的demo
public class YZXDemo {

    private volatile static int num = 0;

    //private static AtomicInteger num = new AtomicInteger(0);


    public static void add(){

        num++;

        //num.getAndIncrement();  //底层用的是CAS

    }

    public static void main(String[] args) {

        //正常理解结果是20000实际结果小于20000
        for (int i = 0; i < 20; i++) {

            new Thread(()->{
                for (int j = 0; j < 1000;j++){
                    add();
                }
            }).start();

        }




        // 判断下存活的线程数 java 默认两个线程jc和 main
        while (Thread.activeCount()>2){
            Thread.yield();

        }

        System.out.println(num);
    }


}

 

 

 

 

 

3、指令重排

/**
 *
 *  一、指令重排是什么:
 *      指令重排是指在程序执行过程中, 为了性能考虑, 编译器和CPU可能会对指令重新排序.
 *      可能写1000万行代码也不会出现指令重排
 *
 *  二、volatile 避免指令重排的原因
 *      添加一个内存的屏障从而禁止指令的重排
 *
 *  三、volatile 哪里用的最多
 *      单例模式的双重锁模式时
 *
 *
 */

 

public class ZLCPDemo {

    int a = 0;
    boolean flag = false;

    public void writer() {
        // 以下两句执行顺序可能会在指令重排等场景下发生变化
        a = 1;
        flag = true;
    }

    public void reader() {
        if (flag) {
            int i = a + 1;
            
        }
    }

}

  

 

posted on 2021-05-23 20:24  与时具进&不忘初心  阅读(69)  评论(0编辑  收藏  举报