JAVA--對象鎖(轉載)__階段總結

來源:互聯網
上載者:User

在並發環境下,解決共用資源衝突問題時,可以考慮使用鎖機制。

1.對象的鎖

所有對象都自動含有單一的鎖。

JVM負責跟蹤對象被加鎖的次數。如果一個對象被解鎖,其計數變為0。在任務(線程)第一次給對象加鎖的時候,計數變為1。每當這個相同的任務(線程)在此對象上獲得鎖時,計數會遞增。

只有首先獲得鎖的任務(線程)才能繼續擷取該對象上的多個鎖。

每當任務離開一個synchronized方法,計數遞減,當計數為0的時候,鎖被完全釋放,此時別的任務就可以使用此資源。

2.synchronized同步塊

synchronized有兩種格式:

格式1:

synchronized(任何對象){

  //訪問共用變數的臨界區(程式段),又稱同步代碼塊

}

格式2:同步化方法。在方法的前面加上synchronized,如:

public synchronized void add() {

//臨界區

}

共用變數所關聯的對象鎖是如何選擇的。即

synchronized (任何對象) {

//臨界區

}

  1.synchronized鎖定的是一個具體對象,通常是共用變數的對象。用synchronized括起來的程式段是訪問該共用變數的臨界區,即synchronized代碼塊。由於所有鎖定同一個對象的線程之間,在synchronized代碼塊上是互斥的,也就是說,這些線程的synchronized代碼塊之間是串列執行的,不再是互相交替穿插並發執行,因而保證了synchronized 代碼塊操作的原子性。但synchronized代碼塊與所有線程的非synchronized 代碼塊之間以及非synchronized代碼塊與非synchronized代碼塊之間都是互相交替穿插並發執行的,故synchronized代碼塊操作的原子性是邏輯上的,而不是物理上的不可打斷。

 2.每一個Java對象都有且只有一個對象鎖。任何時刻,一個對象鎖只能被一個線程所擁有。若兩個或多個線程鎖定的不是同一個對象,則它們的synchronized代碼塊可以互相交替穿插並發執行。

 

3.所有的非synchronized代碼塊或方法,都可自由調用。如線程A獲得了對象鎖,調用需要該對象鎖的synchronized代碼塊,其他線程仍然可以自由調用所有非synchronized方法和代碼

 

4.若線程A獲得了對象O的對象鎖,調用對象O的synchronized代碼塊或方法,則線程A仍然可以調用其他任何需要對象O的鎖的synchronized代碼塊或方法,這是因為線程A已經獲得了對象O的對象鎖了。線程A同時可以調用需要另一個對象K的鎖的synchronized代碼塊或方法,這意味著線程A同時擁有對象O和對象K的對象鎖。

 

5.只有當一個線程執行完它所調用的synchronized代碼塊或方法時,無論是正常執行完,還是異常拋出,該線程才會釋放所擷取的對象鎖。synchronized並不必然地保護資料。程式員應該仔細分析,識別出程式中所有的臨界區,並對這些臨界區施加synchronized機制。若有一處或多處遺漏,則共用變數中資料就會產生錯誤

 

6.臨界區中的共用變數應定義為private型。否則,其他類的方法可能直接存取和操作該共用變數,這樣synchronized的保護就失去了意義。所以只能通過臨界區訪問共用變數。故鎖定的對象通常是this,即通常格式都是:synchronized(this){…}

 

7.一定要保證,所有對共用變數的訪問與操作均在synchronized代碼塊中進行。

 

8.通常共用變數都是執行個體變數。若臨界區中的共用變數是一個類變數,則問題複雜化了,因為類方法與執行個體方法均可訪問類變數。而synchronized鎖定的必須是對象,不能是類。建議是若臨界區中的共用變數是一個類變數,則應該用類方法來訪問操作該類變數。這個類方法成為一個臨界區,必須將該類方法定義為synchronized方法。所有要訪問該共用類變數的執行個體方法,都應該調用定義為synchronized的類方法進行。若執行個體方法一定要想在自己的代碼內部,不通過synchronized的類方法訪問共用類變數,則可通過synchronized(類名.class){…來訪問類鎖。Java中,每一個類都有一個類對象,這個類對象實際上是java.lang.Class的一個執行個體對象,所謂類鎖就是這個類對象的一把鎖。注意類鎖與這個類的執行個體對象的對象鎖雖然都是對象鎖,卻是不同的兩把鎖。所有像synchronized(類名.class()){=同步代碼塊} 這樣鎖定類對象(注意:不是鎖定類的某一個執行個體對象),其中的synchronized代碼塊,都是串列執行的,訪問或使用類鎖要仔細考慮和權衡

 

9.當一個線程進入死亡狀態,線程擁有的所有的對象鎖都被釋放。

3.Lock對象鎖

 

Java SE5引入了java.util.concurrent.lock類庫,這是解決互斥問題的第二種機制。用ReentrantLock類建立一個Lock對象,來保護臨界區。用ReentrantLock保護代碼塊的基本結構如下。

private Lock locker =new ReentrantLock();

locker.lock(); // 加鎖

try{

}finally{

  locker.unlock(); // 解鎖

  }

lock()與unlock()必須配套使用。必須確保lock()對應的unlock()一定會得到執行。因此,必須把unlock()放到finally塊中,確保無論是正常執行,還是異常拋出,unlock()一定會得到執行。

 

 

synchronized和lock的區別:

 

Lock 的鎖定是通過代碼實現的,而 synchronized 是在 JVM 層面上實現的

synchronized 在鎖定時如果方法塊拋出異常,JVM 會自動將鎖釋放掉,不會因為出了異常沒有釋放鎖造成線程死結。但是 Lock 的話就享受不到 JVM 帶來自動的功能,出現異常時必須在 finally 將鎖釋放掉,否則將會引起死結。

 

在資源競爭不是很激烈的情況下,偶爾會有同步的情形下,synchronized是很合適的。原因在於,編譯器通常會儘可能的進行最佳化synchronize,另外可讀性非常好,不管用沒用過5.0多線程包的程式員都能理解。 

ReentrantLock: 
ReentrantLock提供了多樣化的同步,比如有時間限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在資源競爭不激烈的情形下,效能稍微比synchronized差點點。但是當同步非常激烈的時候,synchronized的效能一下子能下降好幾十倍。而ReentrantLock確還能維持常態。 

Atomic: 
和上面的類似,不激烈情況下,效能比synchronized略遜,而激烈的時候,也能維持常態。激烈的時候,Atomic的效能會優於ReentrantLock一倍左右。但是其有一個缺點,就是只能同步一個值,一段代碼中只能出現一個Atomic的變數,多於一個同步無效。因為他不能在多個Atomic之間同步。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.