線程學習四:volatile 關鍵字

很顯然,volatile關鍵字修飾的對象修改後,都能在任何線程中立馬拿到

但是你是否注意到了volatile使用上面的2不能1必須呢?

1、不能將volatile使用到頻繁更新的值上面

  如下圖演示

	public static void main(String args[]) throws InterruptedException {
		for (int i = 0; i < 20; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					for (int i = 0; i < 1000; i++) {
						b++;
					}
				}
			}).start();
		}
		while (Thread.activeCount() > 1) {
			Thread.yield();
		}
		System.out.println(b);
	}
按照道理,我們應該最後輸出20000,但是最後輸出的結果總是小於20000,且每次不固定 。
究其原因,就是因爲對於某線程A,雖然每次我再進行b++操作之前,都能拿到內存中最新的值,但是我在進行b++之後是要把數據寫回到內存中的。 而這時候可能內存中的值已經被其他線程增加了好多次


2、volatile 對象不能與其他非volatile對象聯合使用

     例如 c=(volatile b+d);使用C的時候,及時b及時改變了,C值也不會改變


3、jvm指令重排會影響業務的標誌性對象 必須使用volatile

    例如

private static boolean b = false;

	private static void changeFlag() {
		//doSomeThingCost 10 seconds //代碼1
		b = true; //代碼2
	}

	private static void sayHello() {
		while (true) {
			if(b){
				System.out.println("hello");
			}
		}
	}

	public static void main(String args[]) throws InterruptedException {
		new Thread(new Runnable() {
			@Override
			public void run() {
				changeFlag();
			}
		}).start();

		new Thread(new Runnable() {
			@Override
			public void run() {
				sayHello();
			}
		}).start();
	}

如上代碼,AB線程基本同時執行,B線程要等A中的代碼1執行完後再執行輸出hello .  但是由於JVM的指令重排,可能會導致代碼2在代碼1之前執行,導致提前輸出hello。

這樣的話,如果是銀行等支付付款業務後續處理邏輯,那影響就太大了 。如果加了volatile,那麼JVM的指令重排永遠不能把代碼1放到代碼2後面

順便說一句,內存更新數據是語句級別的,不是方法級別的

驗證代碼如下,輸出爲hello= false; 第一個Thread執行了changeFlag(),然後run()並沒有執行完,b已經修改爲true了。

private volatile static boolean b = false;

	private static void changeFlag() {
		b = true;
	}

	private static void sayHello() {
		System.out.println("hello=" + b);
	}

	public static void main(String args[]) throws InterruptedException {
		new Thread(new Runnable() {
			@Override
			public void run() {
				changeFlag();
				try {
					Thread.sleep(4000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				b = false;
			}
		}).start();
		Thread.sleep(1000);
		new Thread(new Runnable() {
			@Override
			public void run() {
				sayHello();
			}
		}).start();
	}






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章