voliate关键字及其示例

voliate关键字

1 使变量在线程间可见

对于避免不可见性问题,Java还提供了一种弱形式的同步,即使用了volatile关键字。该关键字确保了对一个变量的更新对其他线程可见。当一个变量被声明为volatile时候,线程写入时候不会把值缓存在寄存器或者或者在其他地方,当线程读取的时候会从主内存重新获取最新值,而不是使用当前线程的拷贝内存变量值。volatile虽然提供了可见性保证,但是不能使用他来构建复合的原子性操作,也就是说当一个变量依赖其他变量或者更新变量值时候新值依赖当前老值时候不在适用。与synchronized相似之处在于如图
voliate关键字及其示例_第1张图片
如图线程A修改了volatile变量b的值,然后线程B读取了改变量值,那么所有A线程在写入变量b值前可见的变量值,在B读取volatile变量b后对线程B都是可见的,图中线程B对A操作的变量a,b的值都可见的。volatile的内存语义和synchronized有类似之处,具体说是说当线程写入了volatile变量值就等价于线程退出synchronized同步块(会把写入到本地内存的变量值同步到主内存),读取volatile变量值就相当于进入同步块(会先清空本地内存变量值,从主内存获取最新值)。
转自http://ifeve.com/%E9%AB%98%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%BF%85%E5%A4%87%E5%9F%BA%E7%A1%80/
/**
 * Created by lixiaodong on 2017/6/23.
 */
public class Test extends Thread{

     //voliate
    private String i ="sss";

    private void setI(String i){
        this.i=i;
    }
    @Override
    public void run() {
        System.out.println("进入方法"+i);
        while (i.equals("sss")){
//            System.out.println("方法执行");
//
//            try {
//                Thread.sleep(3000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
        }
        System.out.println("线程结束");
    }

    public static void main(String[] args ) throws InterruptedException{
        Test test=new Test();
        test.start();
        Thread.sleep(1000);
        System.out.println("线程设置了stop");
        test.setI("线程设置了stop");
    }
}

上面是一个简单的示例。

  1. 首先运行代码,可以看到,尽管将变量设置了stop,test线程并没有如预期的停止.说明,test线程内的i的并没有被修改,test只是在start时将i变量拷贝到了线程自有的一块空间内,与主线内的i变量互不影响.
  2. 将voliate关键字放在变量i的声明上,运行发现程序正常停止.可见变量i在任何一个线程内都是可见的,当变量i在主线程被修改时,子线程立即获得了被更新的值.
  3. 最坑的地方来了,打开代码中的while循环中的打印语句,将voliate关键字注释掉,执行代码.神奇的事情发生了,程序正常的停止了,WTF!(在我最开始研究voliate的时候,我一直有这句输出语句,一直得不到正确结果)这是为啥呢?下面这段话基本说明了问题,同时你也可以将输出语句注释点,打开sleep的注释,看看结果.
JVM会尽力保证内存的可见性,即便这个变量没有加同步关键字。换句话说,只要CPU有时间,JVM会尽力去保证变量值的更新。这种与volatile关键字的不同在于,volatile关键字会强制的保证线程的可见性。而不加这个关键字,JVM也会尽力去保证可见性,但是如果CPU一直有其他的事情在处理,它也没办法。最开始的代码,一直处于试了循环中,CPU处于一直被饱受占用的时候,这个时候CPU没有时间,JVM也不能强制要求CPU分点时间去取最新的变量值。而加了System.out.println之后,由于内部代码的同步关键字的存在,导致CPU的输出其实是比较耗时的。这个时候CPU就有可能有时间去保证内存的可见性,于是while循环可以被终止。其实,也可以在while循环里面加上sleep,让run方法放弃cpu,但是不放弃锁,这个时候由于CPU有空闲的时候就去按照JVM的要求去保证内存的可见性。如下图所示。 run方法里面休息了3秒,cpu有充足的空闲时间去取变量的最新值,所以循环执行一次就停止了。(转自http://blog.csdn.net/weililansehudiefei/article/details/70904111)

你可能感兴趣的:(voliate关键字及其示例)