內置鎖(一)synchronized 介紹與用法


一、synchronized 的介紹

  synchronized 是 Java語言的關鍵字,當它用來修飾一個方法或者一個代碼塊的時候,能夠保證在同一時刻最多只有一個線程執行該段代碼,而這段代碼也被稱為臨界區。

  synchronized 有多個叫法,而每個叫法都表明synchronized 的特性:

1、內置鎖(又叫 隱式鎖):synchronized 是內置於JDK中的,底層實現是native;同時,加鎖、解鎖都是JDK自動完成,不需要用戶顯式地控制,非常方便。

2、同步鎖:synchronized 用於同步線程,使線程互斥地訪問某段代碼塊、方法。這就是意味着最多只有一個線程能夠獲得該鎖,當線程A嘗試去獲得線程B持有的內置鎖時,線程A必須等待或者阻塞,知道線程B釋放這個鎖,如果B線程不釋放這個鎖,那么A線程將永遠等待下去。

3、對象鎖:准確來說,是分為對象鎖、類鎖。synchronized 以當前的某個對象為鎖,線程必須通過互斥競爭拿到這把對象鎖,從而才能訪問 臨界區的代碼,訪問結束后,就會釋放鎖,下一個線程才有可能獲取鎖來訪問臨界區(被鎖住的代碼區域)。synchronized鎖 根據鎖的范圍分為 對象鎖 和 類鎖。對象鎖,是以對象實例為鎖,當多個線程共享訪問這個對象實例下的臨界區,只需要競爭這個對象實例便可,不同對象實例下的臨界區是不用互斥訪問;而類鎖,則是以類的class對象為鎖,這個鎖下的臨界區,所有線程都必須互斥訪問,盡管是使用了不同的對象實例;

總的來說對象鎖的粒度要比類鎖的粒度要細,引起線程競爭鎖的情況比類鎖要少的多,所以盡量別用類鎖,鎖的粒度越少越好。
看下面的例子:

    FruitCount fruitCount = new FruitCount();
	FruitCount fruitCount_3 = new FruitCount();
	//線程1、2 使用了同一個FruitCount對象(fruitCount )
    Thread thread_1 = new Thread(new MyRunable(fruitCount));
    Thread thread_2 = new Thread(new MyRunable(fruitCount));
    //線程3使用了不同的FruitCount對象(fruitCount_3 )
    Thread thread_3 = new Thread(new MyRunable(fruitCount_3));

   線程1、2將會互斥訪問getAmount( )方法,線程3則獨享getAmount( )方法;線程1、2的getAmount( )方法中的對象鎖是fruitCount ,線程3的則是 fruitCount_3;這便是對象鎖的粒度范圍,不同的對象,鎖是相互隔離的。而對於setData( )方法,三個線程都要互斥訪問訪問它,因為是同一個鎖 -- FruitCount.class類鎖。

class FruitCount{
	
   static int price = 5;
   static int num = 10;
	
	public void setData(int price,int num){
		//類鎖,以FruitCount.class為鎖
		synchronized(FruitCount.class){
			this.price = price;
			this.num = num;
		}
	}
	
	public int getAmount(){
		//對象鎖,以當前對象為鎖
		synchronized (this) {
			int amount = price*num;
			return amount;
		}
	}
}
class MyRunable implements Runnable{
 
	FruitCount fruitCount;
	public MyRunable(FruitCount fruitCount){
		this.fruitCount = fruitCount;
	}
	
	@Override
	public void run() {
		//setData方法 有類鎖
		fruitCount.setData(5, 10);
		//getAmount方法 里面有對象鎖,就是fruitCount對象
		fruitCount.getAmount();
	}	
}

二、synchronized 用法

synchronized 的用法只有兩種:修飾方法修飾代碼塊

1、在方法聲明時使用,修飾方法

注意:這個synchronized 修飾符 不會參與方法簽名的比較;

語法:

public synchronized void synMethod() {
               //方法體
   }

有以下兩種情況:

  • 1.1、修飾的方法是普通的成員方法,那么是對象鎖,便是以當前對象為鎖,即調用這個方法的對象
  • 1.2、修飾的方法是靜態方法,則是類鎖
public synchronized static int countData(int data){
      return data*data;
}

2、修飾一個代碼塊

語法:

public int synMethod(int a1){
    synchronized( object ) {
            //代碼塊,一次只能有一個線程進入
    }
   }

有以下3種情況:

2.1、object 是 this,是對象鎖,this指代當前對象

public int getAmount(){
		//對象鎖,以當前對象為鎖
		synchronized (this) {
			int amount = price*num;
			return amount;
		}
	}

2.2、object 是一個普通對象實例

  • 如果是靜態對象,那么就是 類鎖
  • 如果是非靜態對象:成員對象變量、局部變量(甚至可以是 方法參數),那么就是對象鎖
public void setObj(){
		FruitCount fruitCount = null;
		//局部變量
		synchronized(fruitCount){
			fruitCount = new FruitCount();
		}
	}

2.3、object 是一個類的class 對象,那么就是類鎖

public void setData(int price,int num){
		//類鎖,以FruitCount.class為鎖
		synchronized(FruitCount.class){
			this.price = price;
			this.num = num;
		}
	}

小 結:

1、出現類鎖的情況:

  • 以 類.class 為鎖
  • 以 靜態變量為鎖
  • 修飾靜態方法

2、出現對象鎖的情況:

  • 以實例成員對象為鎖(特殊:this 指當前對象)
  • 以局部變量(甚至是方法傳進來的參數)為鎖、
  • 修飾成員方法

3、當synchronized修飾方法時,synchronized是不參與 方法簽名的比較;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM