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值,咱們再運行一遍

兩次結果不一致,確切的說每次結果不一致,可是大概範圍不會差距很大

相關文章
相關標籤/搜索