1. 程式人生 > >執行緒同步與synchronized

執行緒同步與synchronized

看如下程式碼:

package project;
public class Demo {
	public static void main(String []args){
		final Out out=new Out();
		new Thread(){
			public void run(){
				int i=0;
				while(i<5){
					i++;
				out.print("1111111111");
				}
			}
		}.start();
		new Thread(){
			public void run(){
				int i=0;
				while(i<5){
					i++;
				out.print("2222222222");
				}
			}
		}.start();
	}
	

}
class Out{
	public void print(String str){
		for(int i=0;i<str.length();i++){
			System.out.print(str.charAt(i));
		}
		System.out.println();
	}
}

執行結果:

 理想的列印應該是每行只有12並且每行10個數字,而上面卻出現了行內數字個數不為10,1中還夾雜著2。

那麼出現這種現象的原因是什麼呢?

執行緒並不是同時執行的,而是CPU快速的在這執行緒之間切換執行,由於切換速度極快使我們感覺同時執行罷了。

在這裡,A執行緒在還沒有執行完print()方法時,就切換到了B執行緒呼叫了print(),然後在B執行緒結束或者還沒結束又切換到了A執行緒,如此反覆,造成的結果。

那麼怎麼控制在A(或B)執行緒在呼叫print()方法時,另一個執行緒不呼叫print()方法呢?

針對執行緒同步問題java早就有解決方法了,最簡單的就是給方法加上synchronized

關鍵字,如下:

class Out{
	public synchronized void  print(String str){
		for(int i=0;i<str.length();i++){
			System.out.print(str.charAt(i));
		}
		System.out.println();
	}
}

這樣結果就會是理想狀態的結果了:

 

這是什麼意思呢?加上synchronized關鍵字後,比如A執行緒執行print()方法就相當於拿到了一把,只有獲取這個鎖才能執行此方法,如果在A執行緒執行print()方法過程中B執行緒也想插一腳進來執行print()方法,對不起此時這是不能夠的,因為此時鎖在A執行緒手裡,B執行緒無權拿到這把鎖,只有等到A執行緒執行完後放棄鎖,B執行緒才能拿到鎖執行print()方法。

為print()方法加上synchronized後其就變成了同步方法,普通同步方法的鎖是this,也就是當前物件,比如Demo中,外部要想呼叫out方法就必須建立Out類例項物件out,此時print()同步方法的鎖就是這個out。

我們還可以通過同步程式碼塊

寫法:synchronized(obj){},其中obj為鎖物件。

如這裡,修改Out類:

class Out{
	public void  print(String str){
		synchronized(this){
		for(int i=0;i<str.length();i++){
			System.out.print(str.charAt(i));
		}
		System.out.println();
		}
	}
}

這樣結果還會是理想狀態:

原因是在執行同步程式碼塊裡面的內容的執行緒 獲得了鎖,只有在它執行完畢後釋放了鎖,其他執行緒才能獲得鎖,並執行程式碼塊裡面的內容。

如上是對一個類的一個物件,裡面的方法或者程式碼塊加鎖。

如果是一個類的多個物件呢?會出現什麼情況?看下面一段程式碼:

package project;
public class Demo {
	public static void main(String []args){
		final Out out1=new Out();
		final Out out2=new Out();
		new Thread(){
			public void run(){
				int i=0;
				while(i<5){
					i++;
				out1.print("1111111111");
				}
			}
		}.start();
		new Thread(){
			public void run(){
				int i=0;
				while(i<5){
					i++;
				out2.print("2222222222");
				}
			}
		}.start();
	}
	

}
class Out{
	public void  print(String str){
		synchronized(this){
		for(int i=0;i<str.length();i++){
			System.out.print(str.charAt(i));
		}
		System.out.println();
		}
	}
}

上面的程式碼了,建立了兩個Out類物件,兩個執行緒分別執行不同的物件的print()方法 。

看下執行結果:

很顯然,結果並不理想,synchronized 加鎖並沒起作用。

原因:synchronized 的鎖物件為this,也就是當前例項物件,但這裡是同一個類的不同物件例項,兩個執行緒訪問的不是同一個物件,所以synchronized並不會起作用。

那麼這裡要怎麼讓synchronized起作用呢?

讓synchronized鎖這個類對應的Class物件。

修改Out類:

class Out{
	public void  print(String str){
		synchronized(Out.class){
		for(int i=0;i<str.length();i++){
			System.out.print(str.charAt(i));
		}
		System.out.println();
		}
	}
}

以上將synchronized(this)改為了synchronized(Out.class)

執行結果如下:

這樣加鎖後,就算不是同一個類的例項物件,鎖也能起作用。