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;
	}
}

上述代码中没有使用volatile关键字,可是在主线程中咱们将flg至为false,可是结果以下:java

 

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关键字,运行结果以下缓存

咱们将睡眠代码注释掉看结果,ide

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;
	}
}

你会发现成功了,这是什么缘由呢?spa

分析:线程

这与JVM实现有一些关系,线程读取CPU缓存有关系,在内存和CPU之间还有一层CPU缓存,CPU运行时会将内存中的变量读取并缓存到CPU缓存中,当主线程改变了内存中的变量时,CPU不在读取内存中的变量,而是直接读的自身缓存,产生了主线程与线程数据不一致的状况。加上volatile关键字是告诉CPU每次去内存中读一下,并缓存到本地(并非不读缓存,是每次从内存中COPY),这样达到了一个变量可见的目的。将睡眠时间去掉会成功,说明主线程先于支线程修改了变量,并非每一次都成功的,个人PC是4核的,因此基本每次都是成功的。code

看下面一个问题,很诡异,将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;
	}
}

看结果:get

正常结束了,不是内存中变量不可见了么,怎么还正常结束呢?it

分析:io

确定是CPU去读过内存了,具体什么时间不知道,这和CPU的工做机制有关,当CPU空闲的时候会读取内存数据缓存到本地,注意观察count值,咱们再运行一遍

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

相关文章
相关标签/搜索