1. 程式人生 > >Android 技能圖譜學習路線系列-Java基礎之方法鎖、物件鎖、類鎖

Android 技能圖譜學習路線系列-Java基礎之方法鎖、物件鎖、類鎖

先了解一下Synchronized的用法。 一、Synchronized的用法 在修飾程式碼塊的時候需要一個reference物件作為鎖的物件。 在修飾方法的時候預設是當前物件作為鎖的物件。 在修飾類時候預設是當前類的Class物件作為鎖的物件。

二、三種鎖得區別與用法 1、方法鎖(synchronized修飾方法時) 在定義方法時加入 synchronized關鍵字來宣告 synchronized 方法。 synchronized 方法控制對類成員變數的訪問: 每個類例項對應一把鎖,每個 synchronized 方法都必須獲得呼叫該方法的類例項的鎖方能執行,否則所屬執行緒阻塞,方法一旦執行,就獨佔該鎖,直到從該方法返回時才將鎖釋放,此後被阻塞的執行緒方能獲得該鎖,重新進入可執行狀態。這種機制確保了同一時刻對於每一個類例項,其所有宣告為 synchronized 的成員函式中至多隻有一個處於可執行狀態,從而有效避免了類成員變數的訪問衝突。 看一下程式碼:

    public class TestSynchronizedMethod {
	private int count = 0;
	
	public static void main(String[] args) {
		TestSynchronizedMethod testSynchronized = new TestSynchronizedMethod();
		
		Thread threadA = new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println("threadA開始執行");
				testSynchronized.methodASynchronized();
				System.out.println("threadA結束");
			}
		});
		threadA.start();
		
		Thread threadB = new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println("threadB開始執行");
				testSynchronized.methodBSynchronized();
				System.out.println("threadB結束");
			}
		});
		threadB.start();
	}
	
	public synchronized void methodASynchronized(){
		System.out.println("methodASynchronized獲得鎖");
		for(int i=0;i<5;i++){
			count++;
			System.out.println("methodASynchronized-count="+count);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("methodASynchronized釋放鎖");
	}
	
	public synchronized void methodBSynchronized(){
		System.out.println("methodBSynchronized獲得鎖");
		for(int i=0;i<5;i++){
			count++;
			System.out.println("methodBSynchronized-count="+count);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("methodBSynchronized釋放鎖");
	}

}

輸出結果:

    threadA開始執行
    methodASynchronized獲得鎖
    methodASynchronized-count=1
    threadB開始執行
    methodASynchronized-count=2
    methodASynchronized-count=3
    methodASynchronized-count=4
    methodASynchronized-count=5
    methodASynchronized釋放鎖
    threadA結束
    methodBSynchronized獲得鎖
    methodBSynchronized-count=6
    methodBSynchronized-count=7
    methodBSynchronized-count=8
    methodBSynchronized-count=9
    methodBSynchronized-count=10
    methodBSynchronized釋放鎖
    threadB結束

從輸出結果可以看出,加了同步鎖的兩個方法,在不同的執行緒中呼叫後,先執行的方法會先獲得鎖,全部執行完畢後才釋放鎖,之後其他方法繼續執行。

2、物件鎖(synchronized修飾方法或程式碼塊) 當一個物件中有synchronized method或synchronized block的時候呼叫此物件的同步方法或進入其同步區域時,就必須先獲得物件鎖。如果此物件的物件鎖已被其他呼叫者佔用,則需要等待此鎖被釋放。(方法鎖也是物件鎖中的一種)

執行緒進入synchronized方法或synchronized 塊的時候獲取該物件的鎖(這個鎖由JVM自動獲取和釋放),如果已經有執行緒獲取了這個物件的鎖,則當前執行緒會等待;synchronized方法或synchronized 塊正常返回或者拋異常而終止,JVM會自動釋放物件鎖。 物件鎖的兩種方式: (1)方法鎖也是物件鎖

   public synchronized void methodASynchronized(){
		System.out.println("methodASynchronized 我是方法鎖也是物件鎖");
	}

(2)物件鎖,程式碼塊形式

public void methodBSynchronized(){
		synchronized (this) {
			System.out.println("methodBSynchronized 我是物件鎖");
		}
	}

看一下程式碼:

      public class TestSynchronizedObject {
	private int count = 0;
	
	public static void main(String[] args) {
		TestSynchronizedObject testSynchronized = new TestSynchronizedObject();
		
		Thread threadA = new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println("threadA開始執行");
				testSynchronized.methodASynchronized();
				System.out.println("threadA結束");
			}
		});
		threadA.start();
		
		Thread threadB = new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println("threadB開始執行");
				testSynchronized.methodBSynchronizedBlock();
				System.out.println("threadB結束");
			}
		});
		threadB.start();
	}
	
	public synchronized void methodASynchronized(){
		System.out.println("methodASynchronized獲得鎖");
		System.out.println("methodASynchronized 我是方法鎖也是物件鎖");
		for(int i=0;i<5;i++){
			count++;
			System.out.println("methodASynchronized-count="+count);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("methodASynchronized釋放鎖");
	}
	
	public void methodBSynchronizedBlock(){
		synchronized (this) {
			System.out.println("methodBSynchronizedBlock獲得鎖");
			System.out.println("methodBSynchronizedBlock 我是物件鎖");
			for(int i=0;i<5;i++){
				count++;
				System.out.println("methodBSynchronizedBlock-count="+count);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			System.out.println("methodBSynchronizedBlock釋放鎖");
		}
	}

}

執行結果如下:

threadA開始執行
methodASynchronized獲得鎖
methodASynchronized 我是方法鎖也是物件鎖
methodASynchronized-count=1
threadB開始執行
methodASynchronized-count=2
methodASynchronized-count=3
methodASynchronized-count=4
methodASynchronized-count=5
methodASynchronized釋放鎖
threadA結束
methodBSynchronizedBlock獲得鎖
methodBSynchronizedBlock 我是物件鎖
methodBSynchronizedBlock-count=6
methodBSynchronizedBlock-count=7
methodBSynchronizedBlock-count=8
methodBSynchronizedBlock-count=9
methodBSynchronizedBlock-count=10
methodBSynchronizedBlock釋放鎖
threadB結束

從輸出結果可以看出,在同一個物件中,不論是同步方法還是同步塊,在不同的執行緒中呼叫後,先執行的方法會先獲得鎖,全部執行完畢後才釋放鎖,之後其他方法繼續執行。

3、類鎖(synchronized 修飾靜態的方法或程式碼塊) 一個class不論被例項化多少次,它的靜態方法或靜態變數在記憶體中都只有一份。所以,一旦一個靜態的方法被申明為synchronized。此類所有的例項化物件在呼叫此方法時共用同一把鎖,稱之為類鎖。

與物件鎖不同的是,物件鎖是用來控制例項方法之間的同步,類鎖是用來控制靜態方法(或靜態變數互斥體)之間的同步。

類鎖的兩種方式: (1)靜態方法

 public static synchronized void methodASynchronizedClass(){
    		System.out.println("methodASynchronizedClass我是靜態方法");
    	}

(2)程式碼塊

public void methodBSynchronizedClass(){
		synchronized (TestSynchronizedClass.class) {
			System.out.println("methodASynchronizedClass我是同步程式碼塊");
		}
	}

看一下程式碼:

public class TestSynchronizedClass {
	private static int count = 0;
	
	public static void main(String[] args) {
		TestSynchronizedClass testSynchronized1 = new TestSynchronizedClass();
		TestSynchronizedClass testSynchronized2 = new TestSynchronizedClass();
		
		Thread threadA = new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println("threadA開始執行");
				testSynchronized1.methodASynchronizedClass();
				System.out.println("threadA結束");
			}
		});
		threadA.start();
		
		Thread threadB = new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println("threadB開始執行");
				testSynchronized2.methodBSynchronizedClass();
				System.out.println("threadB結束");
			}
		});
		threadB.start();
	}
	
	public static synchronized void methodASynchronizedClass(){
		System.out.println("methodASynchronizedClass獲得鎖");
		System.out.println("methodASynchronizedClass我是靜態方法");
		for(int i=0;i<5;i++){
			count++;
			System.out.println("methodASynchronizedClass-count="+count);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("methodASynchronizedClass釋放鎖");
	}
	
	public void methodBSynchronizedClass(){
		synchronized (TestSynchronizedClass.class) {
			System.out.println("methodBSynchronizedClass獲得鎖");
			System.out.println("methodASynchronizedClass我是同步程式碼塊");
			for(int i=0;i<5;i++){
				count++;
				System.out.println("methodBSynchronizedClass-count="+count);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			System.out.println("methodBSynchronizedClass釋放鎖");
		}
	}

}

執行結果如下:

threadA開始執行
methodASynchronizedClass獲得鎖
methodASynchronizedClass我是靜態方法
methodASynchronizedClass-count=1
threadB開始執行
methodASynchronizedClass-count=2
methodASynchronizedClass-count=3
methodASynchronizedClass-count=4
methodASynchronizedClass-count=5
methodASynchronizedClass釋放鎖
threadA結束
methodBSynchronizedClass獲得鎖
methodASynchronizedClass我是同步程式碼塊
methodBSynchronizedClass-count=6
methodBSynchronizedClass-count=7
methodBSynchronizedClass-count=8
methodBSynchronizedClass-count=9
methodBSynchronizedClass-count=10
methodBSynchronizedClass釋放鎖
threadB結束

從輸出結果可以看出,在同一個class物件的不同例項中,不論是同步方法還是同步塊,在不同的執行緒中呼叫後,先執行的方法會先獲得類鎖,全部執行完畢後才釋放鎖,之後其他方法繼續執行。