Java - 執行緒安全

襲冷發表於2014-06-30

一、競態條件&臨界區
        在同一程式中執行多個執行緒本身不會導致問題,問題在於多個執行緒訪問了相同的資源。如,同一記憶體區(變數,陣列,或物件)、系統(資料庫,web services等)或檔案。實際上,這些問題只有在一或多個執行緒向這些資源做了寫操作時才有可能發生,只要資源沒有發生變化,多個執行緒讀取相同的資源就是安全的。
        當兩個執行緒競爭同一資源時,如果對資源的訪問順序敏感,就稱存在競態條件。導致競態條件發生的程式碼區稱作臨界區。上例中add()方法就是一個臨界區,它會產生競態條件。在臨界區中使用適當的同步就可以避免競態條件。

二、詳解Java同步
        Java中的同步塊用synchronized標記。同步塊在Java中是同步在某個物件上。所有同步在一個物件上的同步塊在同時只能被一個執行緒進入並執行操作。所有其他等待進入該同步塊的執行緒將被阻塞,直到執行該同步塊中的執行緒退出。
    1、例項方法同步

public synchronized void add(int value){
	this.count += value;
}

        Java例項方法同步是同步在擁有該方法的物件上。這樣,每個例項其方法同步都同步在不同的物件上,即該方法所屬的例項。只有一個執行緒能夠在例項方法同步塊中執行。如果有多個例項存在,那麼一個執行緒一次可以在一個例項同步塊中執行操作。


    2、靜態方法同步

public static synchronized void add(int value){				
	count += value;
}
        靜態方法的同步是指同步在該方法所在的類物件上。因為在Java虛擬機器中一個類只能對應一個類物件,所以同時只允許一個執行緒執行同一個類中的靜態同步方法。
        對於不同類中的靜態同步方法,一個執行緒可以執行每個類中的靜態同步方法而無需等待。不管類中的那個靜態同步方法被呼叫,一個類只能由一個執行緒同時執行。

    3、例項方法中的同步塊

	public  synchronized void add(int value){

		synchronized (this) {
			count += value;
		}
		
	}

        示例使用Java同步塊構造器來標記一塊程式碼是同步的。該程式碼在執行時和同步方法一樣。
        示例中使用了“this”,即為呼叫add方法的例項本身。在同步構造器中用括號括起來的物件叫做監視器物件。上述程式碼使用監視器物件同步,同步例項方法使用呼叫方法本身的例項作為監視器物件。
        一次只有一個執行緒能夠在同步於同一個監視器物件的Java方法內執行。
        在下面的兩個方法的示例中,同步都是在他們所呼叫的例項物件上,所以每次只有一個執行緒能夠在兩個同步塊中任意一個方法內執行。

	public synchronized void log1(String msg1, String msg2) {
		System.out.println(msg1 + "-" + msg2);
	}

	public void log2(String msg1, String msg2) {
		synchronized (this) {
			System.out.println(msg1 + "-" + msg2);
		}
	}
    4、靜態方法中的同步塊
         和上面類似,下面是兩個靜態方法同步的例子。這些方法同步在該方法所屬的類物件上。

	public static synchronized void log1(String msg1, String msg2) {
		System.out.println(msg1 + "-" + msg2);
	}

	public static void log2(String msg1, String msg2) {
		synchronized (MyTest.class) {
			System.out.println(msg1 + "-" + msg2);
		}
	}

        這兩個方法不允許同時被執行緒訪問。
        如果第二個同步塊不是同步在ThisClass.class這個物件上。那麼這兩個方法可以同時被執行緒訪問。


三、參考

    Java併發性和多執行緒-併發程式設計網


 


相關文章