public class Thread02 implements Runnable { /**volatile*/ boolean flg = true; @Override public void run() { System.out.println(Thread.currentThread().getName()+":start"); while(flg){ //System.out.println("do something"); } System.out.println(Thread.currentThread().getName()+":end"); } public static void main(String[] args) { Thread02 test = new Thread02(); Thread t1 = new Thread(test); t1.start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } test.flg=false; } }
上述代码中没有使用volatile关键字,但是在主线程中我们将flg至为false,但是结果如下:

public class Thread02 implements Runnable { volatile boolean flg = true; @Override public void run() { System.out.println(Thread.currentThread().getName()+":start"); while(flg){ //System.out.println("do something"); } System.out.println(Thread.currentThread().getName()+":end"); } public static void main(String[] args) { Thread02 test = new Thread02(); Thread t1 = new Thread(test); t1.start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } test.flg=false; } }
我们将flg变量前加上volatile关键字,运行结果如下

我们将睡眠代码注释掉看结果,
public class Thread02 implements Runnable { volatile boolean flg = true; @Override public void run() { System.out.println(Thread.currentThread().getName()+":start"); while(flg){ //System.out.println("do something"); } System.out.println(Thread.currentThread().getName()+":end"); } public static void main(String[] args) { Thread02 test = new Thread02(); Thread t1 = new Thread(test); t1.start(); // try { // TimeUnit.SECONDS.sleep(1); // } catch (InterruptedException e) { // e.printStackTrace(); // } test.flg=false; } }

你会发现成功了,这是什么原因呢?
分析:
这与JVM实现有一些关系,线程读取CPU缓存有关系,在内存和CPU之间还有一层CPU缓存,CPU运行时会将内存中的变量读取并缓存到CPU缓存中,当主线程改变了内存中的变量时,CPU不在读取内存中的变量,而是直接读的自身缓存,产生了主线程与线程数据不一致的情况。加上volatile关键字是告诉CPU每次去内存中读一下,并缓存到本地(并不是不读缓存,是每次从内存中COPY),这样达到了一个变量可见的目的。将睡眠时间去掉会成功,说明主线程先于支线程修改了变量,并不是每一次都成功的,我的PC是4核的,所以基本每次都是成功的。
看下面一个问题,很诡异,将while循环中的注释掉的代码打开,去掉volatile关键字,开启睡眠
public class Thread02 implements Runnable { /*volatile*/ boolean flg = true; int count = 0; @Override public void run() { System.out.println(Thread.currentThread().getName()+":start"); while(flg){ count++; System.out.println(count+":do something"); } System.out.println(Thread.currentThread().getName()+":end"); } public static void main(String[] args) { Thread02 test = new Thread02(); Thread t1 = new Thread(test); t1.start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } test.flg=false; } }
看结果:

正常结束了,不是内存中变量不可见了么,怎么还正常结束呢?
分析:
肯定是CPU去读过内存了,具体什么时间不知道,这和CPU的工作机制有关,当CPU空闲的时候会读取内存数据缓存到本地,注意观察count值,我们再运行一遍

两次结果不一致,确切的说每次结果不一致,但是大概范围不会差距很大