java多線程總結-同步之volatile關鍵字


1 案例之變量內存可見性

代碼解析:新起一個子線程執行m()方法,1秒后主線程將b置為false,子線程是否會停止執行死循環while(b){},打印“end”

package com.bernardlowe.concurrent.t01;

import java.util.concurrent.TimeUnit;

public class Test_09 {
	
	boolean b = true;
	
	void m(){
		System.out.println("start");
		while(b){}
		System.out.println("end");
	}
	
	public static void main(String[] args) {
		final Test_09 t = new Test_09();
		new Thread(new Runnable() {
			@Override
			public void run() {
				t.m();
			}
		}).start();
		
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		t.b = false;
	}
	
}

**結果:1秒鍾過后並不會停止執行死循環while(b){},打印“end” **
這時候,如果將boolean b = true;這段代碼前加一個volatile關鍵字
volatile boolean b = true;,就會達到預想中的效果

思考:為什么加上這個關鍵字,其他線程就會讀取到已經改變的變量的值了?

是因為在CPU計算過程中,會將計算過程需要的數據加載到CPU計算緩存中,當CPU計算中斷時,有可能刷新緩存,重新讀取內存中的數據。在線程運行的過程中,如果某變量被其他線程修改,可能造成數據不一致的情況,從而導致結果錯誤。
而volatile修飾的變量是線程可見的,當JVM解釋volatile修飾的變量時,會通知CPU,在計算過程中,每次使用變量參與計算時,都會檢查內存中的數據是否發生變化,而不是一直使用CPU緩存中的數據,可以保證計算結果的正確。

但是這樣還有一個問題,volatile只能保證可見性,不能保證原子性

2 案例之變量的原子性

下面再看一個示例:
預期結果:起10個線程,每個線程都對count增加10000,預期結果為count=100000

package com.bernardlowe.concurrent.t01;

import java.util.ArrayList;
import java.util.List;

public class Test_10 {
	
	volatile int count = 0;
	/*synchronized*/ void m(){
		for(int i = 0; i < 10000; i++){
			count++;
		}
	}
	
	public static void main(String[] args) {
		final Test_10 t = new Test_10();
		List<Thread> threads = new ArrayList<>();
		for(int i = 0; i < 10; i++){
			threads.add(new Thread(new Runnable() {
				@Override
				public void run() {
					t.m();
				}
			}));
		}
		for(Thread thread : threads){
			thread.start();
		}
		for(Thread thread : threads){
			try {
				thread.join();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println(t.count);
	}
}

但結果並不是

原因是volatile只是通知底層計算時,CPU檢查內存數據,而不是讓一個變量在多個線程中同步。

這時候可以給m()方法增加一個synchronized關鍵字,可以達到預期的效果,即synchronized void m()

還有另一種方法可以保證原子性,在上面代碼將count聲明為AtomicInteger原子操作,結果仍然是100000

	// 其中的每個方法都是原子操作。可以保證線程安全。
	AtomicInteger count = new AtomicInteger(0);
	void m(){
		for(int i = 0; i < 10000; i++){
			count.incrementAndGet();
		}
	}

這里不僅僅可聲明Integer類型,java.util.concurrent.atomic包里面還有其他類型的


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM