1. 程式人生 > >第二章 物件及變數的併發訪問

第二章 物件及變數的併發訪問

1 怎麼解釋什麼是“執行緒安全”與“非執行緒安全”?

非執行緒安全:多個執行緒對同一物件中的例項變數進行併發訪問時發生,會產生“髒讀”的後果,也就是取得的資料是被更改的;

執行緒安全:獲得的例項變數的值是經過同步處理的,不會出現“髒讀”現象

2.1.1方法內的變數為執行緒安全

public class HasSelfPrivateNum {
	private int num = 0;
	public void addI(String username) {
			try {
				if(username.equals("a")) {
				num = 100;
				System.out.println("a set over");
				Thread.sleep(2000);
			}else{
				num = 200;
				System.out.println("b set over");
			}
				System.out.println(username+"num = "+num);
				}catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

	}
}
//ThreadA
public class ThreadA extends Thread {
	private HasSelfPrivateNum numRef;
	public ThreadA(HasSelfPrivateNum numRef) {
		super();
		this.numRef = numRef;
	}
	public void run() {
		super.run();
		numRef.addI("a");
	}
}
//ThreadB
public class ThreadB extends Thread {
	private HasSelfPrivateNum numRef;
	public ThreadB(HasSelfPrivateNum numRef) {
		super();
		this.numRef = numRef;
	}
	public void run() {
		super.run();
		numRef.addI("b");
	}
}
//本例用來說明兩個執行緒呼叫統一物件的同一例項變數時,可能會出現“非執行緒安全問題”
public class Run {
	public void main(String[] args) {
		HasSelfPrivateNum numRef = new HasSelfPrivateNum();
		ThreadA athread = new ThreadA(numRef);
		athread.start();
		ThreadB bthread = new ThreadB(numRef);
		bthread.start();
	}
}

2.1.2 例項變數非執行緒安全

只要把上例中的num的宣告移到方法外即為“非執行緒安全的”例子,處理方式:在addI方法前加“synchronized”關鍵字

ps:在兩個執行緒訪問同一物件中的同步方法時一定是執行緒安全的

2.1.3 多個物件多個鎖

public class Run1 {
	public static void main(String[] args) {
		// 兩個物件被兩個不同執行緒訪問
		HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
		HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();
		ThreadA threada = new ThreadA(numRef1);
		ThreadB threadb = new ThreadB(numRef2);
		threada.start();
		threadb.start();
		}
}

對於多個執行緒訪問同一物件時,哪個執行緒先執行帶synchronized關鍵字的方法,哪個執行緒就持有該方法所屬物件的鎖Lock,而其他執行緒只能處於等待狀態;如果是多個執行緒訪問多個物件時,JVM會建立多個鎖。

2.1.4 synchronized方法與鎖物件

public class MyObject {
	public void methodA() {
		System.out.println(Thread.currentThread().getName());
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

當將methodA方法加上synchronized時,執行緒排隊執行

對於共享資源的讀寫訪問才需要同步化

2.1.5 髒讀

髒讀:在讀取例項變數時此值已經被其他執行緒更改過了

解決方法:通過synchronized關鍵字解決