java多线程-volatile

大纲:

  1. java内存模型
  2. 可见性、原子性、有序性
  3. volatile关键字

 

一、java内存模型

java所有变量值都存在主内存里,每一个线程又拥有自己的工作内存,线程对变量的读写都在工作内存里完成,工作内存间相互隔离。

 

二、可见性、原子性、有序性

  • 可见性:指当多个线程访问同一变量时,一个线程修改了这个变量的值,其他线程能访问这个变量是永远能够获取更新后的值。

       保证可见性2种方式:1代码块加锁。2volatile关键字。

  • 原子性:多个操作,要么全部执行,要么全部不执行。这些操作执行时不受外界干扰。
i = 1; //1
i = x; //2
i++;  //3
//注意:上面的操作中,只有1是原子的。需要更大范围的原子操作需要加锁。
  • 有序性:处理器会对指令进行重新排序,排序后的结果不会影响单线程结果,但有可能会影响多线程执行结果。指令重排会遵循happens-before 原则,其中volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作。就是说对一个变量,一个线程写,一个读,写操作一定在读操作前面。

 

三、volatile关键字

  volatile相当于加入一个内存屏障,内存屏障保证3件事:

  1. 工作内存中修改的内容立即刷新到主内存。
  2. 当执行写操作时,其他工作内存中被写入的那个变量的缓存立即无效。
  3. 保证屏障后面的指令重排后出现在屏障前面,前面的指令不会到屏障后面。执行到内存屏障这条指令时,前面的代码都已经执行完毕。
    a=1;
    b=2;
    test=true; //被volatile修饰
    c=3;
    d=4;
    //test如果不被volatile修饰,指令重排可以将上面的5条赋值任意排序
    //如果被volatile修饰,指令重拍只能在ab间重排、cd间重排而且执行test赋值时、ab赋值已经完成。

  因此,volatile保证可见性,和一定的有序性,不保证原子性。

 

四、例

/**
 * 当flag不被volatile修饰的时候会出现init ok后thread2卡死的情况。
 */
class TestVolatile {
    private static volatile boolean flag = false;

    public static void main(String[] args) throws InterruptedException {
        Thread thread2 = new Thread(()->{
            while (true){
                if(flag)
                    System.out.println(1);
            }
        });
        thread2.start();

        Thread thread1 = new Thread(()->{
            flag = true;
            System.out.println("init ok");
        });
        thread1.start();
    }
}

 

posted @ 2018-10-25 09:36  扶不起的刘阿斗  阅读(213)  评论(0编辑  收藏  举报