volatile关键字及内存可见性

Posted wq3435

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了volatile关键字及内存可见性相关的知识,希望对你有一定的参考价值。

先看一段代码:

  

package com.java.juc;

public class TestVolatile {

    public static void main(String[] args) {
        ThreadDemo td = new ThreadDemo();
        new Thread(td).start();
        while(true){
            if(td.isFlag()){
                System.out.println("----------------");
                break;
            }
        }
    }

}

class ThreadDemo implements Runnable{

    private boolean flag = false;
    public void run() {
        try {
            Thread.sleep(200);
            flag = true;
            System.out.println("flag= "+ isFlag());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    /**
     * @return the flag
     */
    public boolean isFlag() {
        return flag;
    }
    /**
     * @param flag the flag to set
     */
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
    
    
}

 

运行这段代码,运行的结果只有:

  flag= true

这是由于 子线程在操作共享数据时,会将主存总的flag,复制一份到线程的缓存中进行操作,操作完成后会再将数据写到主存中,由于while(true)是一个运行效率非常高的一句代码,而且运行速度非常快,导致主线程再执行循环时没有机会从主存中读取到数据,导致flag的值是主线程最初读到的值,flag = false;

 

在共享变量上加一个volatile关键字,会解决这个问题

  

package com.java.juc;

public class TestVolatile {

    public static void main(String[] args) {
        ThreadDemo td = new ThreadDemo();
        new Thread(td).start();
        while(true){
            if(td.isFlag()){
                System.out.println("----------------");
                break;
            }
        }
    }

}

class ThreadDemo implements Runnable{

    private volatile boolean flag = false;
    public void run() {
        try {
            Thread.sleep(200);
            flag = true;
            System.out.println("flag= "+ isFlag());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    /**
     * @return the flag
     */
    public boolean isFlag() {
        return flag;
    }
    /**
     * @param flag the flag to set
     */
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
    
    
}

----------------
flag= true

 

 

volatile 关键字的作用:当多个线程操作共享数据时,可以保证内存中的数据是可见的。

  可以使用内存再来解释,可以认为各个线程操作各自独立缓存中的数据是实时与主存进行同步的,可以理解为多个线程直接在主存中操作数据,而不是在各自的缓存中。

  加了volatile后性能还是比什么都不加要讲的,但是比锁的效率要高。

加了volatile之后效率会低在哪?

  实际上JVM底层有个优化,叫指令重排序,使用volatile修饰后就不能重排序了。

  

加锁的效率是最低的(使用synchronized关键字),同步锁会使线程每次从主存中读取数据,但是同步锁的效率很低。有多个线程来访问时先判断锁,如果被其他线程锁挡住,则线程会进入阻塞挂起状态,那得等到下次cpu再次分配任务,才能执行。

 

当然:volatile和synchronized还是不同的。volatile相较与synchronized是一种较为轻量级的同步策略。

注意:

  1. volatile 不具备 "互斥性"

  2. volatile 不能保证变量的 "原子性"

 

以上是关于volatile关键字及内存可见性的主要内容,如果未能解决你的问题,请参考以下文章

24.volatile关键字的作用volatile原理可见性内存屏障volatile性能transient

java内存模型及volatile关键字

java内存模型及volatile关键字

volatile关键字 - 内存可见性

你真的了解volatile关键字吗?

java多线程 -- volatile 关键字 内存 可见性