JUC 学习(一) 之 volatile 关键字

JUC 学习(一) 之 volatile 关键字

首先我们来看这么一段代码:

package indi.qiaolin.juc;

import lombok.Data;

/**
 *  测试JUC volatile 关键字
 *
 *  @author  qiaolin
 *  @version 2018年7月8日
 *
 */

public class VolatileTest {
    public static void main(String[] args) {
        ThreadDemo td = new ThreadDemo();
        new Thread(td).start();

        while(true){
            if(td.isFlag()){
                System.out.println("VolatileTest.main");
                break;
            }
        }
    }
}


@Data
class ThreadDemo implements Runnable{
    private boolean flag;

    public void run() {
        setFlag(true);
    }
}

你觉得最后的结果是不是控制台输出:VolatileTest.main 并且程序结束?

其实这个程序有80%的几率不会结束!陷入下面这个死循环!

while(true){
  if(td.isFlag()){ 
        System.out.println("VolatileTest.main");
        break;
    }
}

而陷入死循环的可能只有一种就是td这个对象的flag属性一直为false; 但是我们明明在td的run方法中设置成true了的啊!

public void run() {
   setFlag(true);
}

其实造成这个问题的情况就是 多个线程共享资源不可见的问题!每一个线程访问共享数据时都会先把数据读过来,然后如果有修改值的操作,就把改好的数据放回去!假设这个数据是在堆中!我们两个线程一个读一个写,那么这两个线程都是先把数据读到自己的缓存中,然后再操作,这时候如果A是读,B是写,那么如果A先读了数据,B后面去写,然后B把写好的数据放回到堆中,照理说A需要刷新他缓存中的数据,但是A如果处于一个高效率执行的情况下,例如 while(true) 他就会没时间去刷新或者刷新很慢这种情况,导致A所在的数据并不是已经被B修改过的数据,这个时候就出现了这个情况 内存可见性 !


下面是比较详细内存可见性的解释:
1、内存可见性(Memory Visibility)是指当某个线程正在使用对象状态而另一个线程在同时修改该状态,需要确保当一个线程修改了对象状态后,其他线程能够看到发生的状态变化。
2、可见性错误是指当读操作与写操作在不同的线程中执行时,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情。


来一套图说明下这个问题把!如果有错请提出来!多多交流指教!!

首先这两句代码创建了一个td对象, 并启动一个流程;我们暂且称这个流程为流程1

ThreadDemo td = new ThreadDemo();
new Thread(td).start();

这里写图片描述

流程1虽然已经启动,但是他肯定没有抢到执行权,此时main线程继续走,进入了这个while循环,但是while(true)的效率,那是相当的高,主要是他不用去调用其他对象或者运算!

 while(true){
     if(td.isFlag()){
          System.out.println("VolatileTest.main");
          break;
      }
  }

此时main 已经读了flag到自己的缓存
这里写图片描述

可能在执行中 线程1抢到了执行权
这里写图片描述
这里写图片描述
这里写图片描述
他先读取了flag数据,然后修改,然后写回去!

但是main线程在高效率的情况下,他一直猛干,根本就不去刷新,拿到的还是缓存的值 就导致了main线程中的td.isFlag()始终是false!
不过我运行这个程序多次,我发现有时候还是会去刷新的,并不是百分百的会死循环!但是有一定的几率!于是我又做了个实验,让他这个代码变得慢一点循环!

 while(true){
    if(td.isFlag()){
        System.out.println("VolatileTest.main");
        break;
    }
    System.out.println("main");
}

加了一句输出,你就会发现,他居然没那么容易死循环了!
这里写图片描述

但原因并不是效率太高的问题,而是他们两个线程不可见的问题,那我们每次都要多加上几句其他代码来解决? 这肯定不是解决方案啊!这么low,怎么阔能!
解决方案有两种:
1、加上 synchronized 关键字,这个同步关键字会刷新当前线程的缓存!
2、我们只需要把多个线程共享的数据使用volatile关键字, volatile 变量,用来确保将变量的更新操作通知到其他线程。可以将volatile 看做一个轻量级的锁,但是又与锁有些不同;对于多线程,不是一种互斥关系(当前所对象只能进来一个)但是又不能保证变量状态的“原子性操作”

如果你只是想让一个属性多个线程可见的话就用 volatile把!
例:

@Data
class ThreadDemo implements Runnable{
    private volatile boolean flag;

    public void run() {
        setFlag(true);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_35170213/article/details/80999151