JAVA多線程synchronized

synchronized 關鍵字,它包括兩種用法:synchronized 方法和 synchronized 塊。  
1. synchronized 方法:通過在方法聲明中加入 synchronized關鍵字來聲明 synchronized 方法。如:  
public synchronized void accessVal(int newVal);  
synchronized 方法控制對類成員變量的訪問:每個類實例對應一把鎖,每個 synchronized 方法都必須獲得調用該方法的類實例的鎖方能

執行,否則所屬線程阻塞,方法一旦執行,就獨佔該鎖,直到從該方法返回時纔將鎖釋放,此後被阻塞的線程方能獲得該鎖,重新進入可執行

狀態。這種機制確保了同一時刻對於每一個類實例,其所有聲明爲 synchronized 的成員函數中至多隻有一個處於可執行狀態(因爲至多隻有

一個能夠獲得該類實例對應的鎖),從而有效避免了類成員變量的訪問衝突(只要所有可能訪問類成員變量的方法均被聲明爲 synchronized)

。  

/* @author Jessica
 * 
 * 對同一個實例來說,任一時刻只能有一個synchronized方法在執行。當一個方法正在執行某個synchronized方法時,
 * 其他線程如果想要執行這個實例的任意一個synchronized方法,都必須等待當前執行 synchronized方法的線程退出此方法後,
 * 才能依次執行。
 */
public class RunnableTwoMethod implements Runnable{
	
	public synchronized void printVal(){
		for (int i = 1; i <= 5; i++) {
			System.out.println(Thread.currentThread().getName()+":"+String.valueOf(i));
		}
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public synchronized void printVal2(){
		for (int i = 10; i > 5; i--) {
			System.out.println(Thread.currentThread().getName()+":"+String.valueOf(i));
		}
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public void run(){
		this.printVal();
		this.printVal2();
	}

	public static void main(String[] args) {
		RunnableTwoMethod mythread = new RunnableTwoMethod(); 
		Thread f1 = new Thread(mythread);
		f1.start(); 
		Thread f2 = new Thread(mythread);
		f2.start(); 
	}

}
運行結果:Thread-0:1
          Thread-0:2
          Thread-0:3
          Thread-0:4
          Thread-0:5
          Thread-0:10
          Thread-0:9
          Thread-0:8
          Thread-0:7
          Thread-0:6
          Thread-1:1
          Thread-1:2
          Thread-1:3
          Thread-1:4
          Thread-1:5
          Thread-1:10
          Thread-1:9
          Thread-1:8
          Thread-1:7
          Thread-1:6
在這個例子中,Thread-0首先搶佔了CPU獲得執行權,當他執行printVal()方法時,線程Thread-1阻塞,只有當Thread-0釋放鎖之後,Thread-1才獲得鎖並執行。

/**
 * @author Jessica
 * 
 * 對同一個實例來說,任一時刻只能有一個synchronized方法在執行。當一個方法正在執行某個synchronized方法時,
 * 其他線程如果想要執行這個實例的任意一個synchronized方法,都必須等待當前執行 synchronized方法的線程退出此方法後,
 * 才能依次執行。但是,非synchronized方法不受影響,不管當前有沒有執行synchronized方法,
 * 非synchronized方法都可以被多個線程同時執行。
 *
 */
public class RunnableTwoMethod implements Runnable{
	
	public synchronized void printVal(){
		for (int i = 1; i <= 5; i++) {
			System.out.println(Thread.currentThread().getName()+":"+String.valueOf(i));
		}
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public void printVal3(){
		for (int i = 15; i > 10; i--) {
			System.out.println(Thread.currentThread().getName()+":"+String.valueOf(i));
		}
		
	}
	
	public void run(){
		this.printVal();
		this.printVal3();
	}

	public static void main(String[] args) {
		RunnableTwoMethod mythread = new RunnableTwoMethod(); 
		Thread f1 = new Thread(mythread);
		f1.start(); 
		Thread f2 = new Thread(mythread);
		f2.start(); 
	}

}
運行結果:Thread-0:1
Thread-0:2
Thread-0:3
Thread-0:4
Thread-0:5
Thread-0:15
Thread-0:14
Thread-0:13
Thread-0:12
Thread-1:1
Thread-1:2
Thread-1:3
Thread-0:11
Thread-1:4
Thread-1:5
Thread-1:15
Thread-1:14
Thread-1:13
Thread-1:12
Thread-1:11
只有同一實例的synchronized方法同一時間只能被一個線程執行,不同實例的synchronized方法是可以併發的

/**
 * @author Jessica
 * 
 * 只有同一實例的synchronized方法同一時間只能被一個線程執行,不同實例的synchronized方法是可以併發的
 *
 */
public class FooTwoMethod extends Thread{
	
	public synchronized void printVal(){
		for (int i = 1; i <= 5; i++) {
			System.out.println(Thread.currentThread().getName()+":"+String.valueOf(i));
		}
	}
	
	public synchronized void printVal2(){
		for (int i = 10; i > 5; i--) {
			System.out.println(Thread.currentThread().getName()+":"+String.valueOf(i));
		}
	}
	
	public void run(){
		this.printVal();
		this.printVal2();
	}

	public static void main(String[] args) {
		FooTwoMethod f1 = new FooTwoMethod(); 
		f1.start(); 
		FooTwoMethod f2 = new FooTwoMethod();
		f2.start(); 
	}

}

運行結果:Thread-0:1
Thread-1:1
Thread-0:2
Thread-0:3
Thread-0:4
Thread-0:5
Thread-0:10
Thread-0:9
Thread-0:8
Thread-0:7
Thread-0:6
Thread-1:2
Thread-1:3
Thread-1:4
Thread-1:5
Thread-1:10
Thread-1:9
Thread-1:8
Thread-1:7
Thread-1:6

在 Java 中,不光是類實例,每一個類也對應一把鎖,這樣我們也可將類的靜態成員函數聲明爲 synchronized ,以控制其對類的靜態成

員變量的訪問。  
synchronized 方法的缺陷:若將一個大的方法聲明爲synchronized 將會大大影響效率,典型地,若將線程類的方法 run() 聲明爲

synchronized ,由於在線程的整個生命期內它一直在運行,因此將導致它對本類任何 synchronized 方法的調用都永遠不會成功。當然我們可

以通過將訪問類成員變量的代碼放到專門的方法中,將其聲明爲 synchronized ,並在主方法中調用來解決這一問題,但是 Java 爲我們提供

了更好的解決辦法,那就是 synchronized 塊。  
2. synchronized 塊:通過 synchronized關鍵字來聲明synchronized 塊。語法如下:  
synchronized(syncObject) {  
//允許訪問控制的代碼  
}  
synchronized 塊是這樣一個代碼塊,其中的代碼必須獲得對象 syncObject (如前所述,可以是類實例或類)的鎖方能執行,具體機

制同前所述。由於可以針對任意代碼塊,且可任意指定上鎖的對象,故靈活性較高。  
對synchronized(this)的一些理解 
一、當兩個併發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程得到執行。另一個線

程必須等待當前線程執行完這個代碼塊以後才能執行該代碼塊。  
二、然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized

(this)同步代碼塊。  
三、尤其關鍵的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)

同步代碼塊的訪問將被阻塞。  
四、第三個例子同樣適用其它同步代碼塊。也就是說,當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就獲得了這個

object的對象鎖。結果,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞。  
五、以上規則對其它對象鎖同樣適用


發佈了50 篇原創文章 · 獲贊 48 · 訪問量 45萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章