Java Learning:並發中的同步鎖(synchronized)


 

引言

   最近一段時間,實驗室已經傾巢出動找實習了,博主也湊合了一把,結果有悲有喜,BAT理所應當的跪了,也收到了其他的offer,總的感受是有必要夯實基礎啊。
  言歸正傳,最近在看到java多線程的時候,發現線程很多都是用了synchronized(同步鎖)的關鍵字,對它的了解還只停留在“鎖”的概念上。博主也在網上搜了一些介紹synchronized 的博客,但是越看越糊塗,而且我的水平還沒有到能夠鑒別真偽,所以為了避免“練錯神功,走火入魔“,我捧起了《Thinking in Java》。本文就是針對此書關於synchronized 的內容,加入了一些自己的代碼驗證,不求能夠駕輕就熟,只能力求不出錯,不誤導大家,如果有些地方存在問題,也請大家海涵,指出意見。 
 

為什么要使用同步鎖?

   在《Thinking in Java》中,是這么說的:對於並發工作,你需要某種方式來防止兩個任務訪問相同的資源(其實就是共享資源競爭)。  防止這種沖突的方法就是當資源被一個任務使用時,在其上加鎖。第一個訪問某項資源的任務必須鎖定這項資源,使其他任務在其被解鎖之前,就無法訪問它了,而在其被解鎖之時,另一個任務就可以鎖定並使用它了。
 
   基本上所有的並發模式在解決線程沖突問題的時候,都是采用序列化訪問共享資源的方案。這意味在給定時刻只允許一個任務訪問共享資源,通常這是通過在代碼前面加上一條鎖語句來實現的,鎖語句產生了一種互相排斥的效果,這種機制稱為互斥量(mutex)。
 

同步鎖的實現原理?

  所有對象都自動含有單一的鎖(監視器),當在對象上調用其任意synchronized 方法的時候,此對象都被加鎖。 對於某個特定對象來說,其所有synchronized方法共享同一個鎖 ,這可以被用來防止多個任務同時訪問被編碼為對象內存。
  針對特定對象所有synchronized方法共享同一個鎖,我想重點介紹一下:
 
1、當兩個並發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程得到執行。另一個線程必須等待當前線程執行完這個代碼塊以后才能執行該代碼塊。
 
2、當一個線程訪問object的一個synchronized(this) 同步代碼塊時,其他線程對object中所 有其它synchronized(this) 同步代碼塊的訪問將被阻塞。
 
3、當一個線程訪問object的一個synchronized(this) 同步代碼塊時,它就獲得了這個object 的對象鎖。結果,其它線程對該object 對象所有同步代碼部分的訪問都被暫時阻塞

   重點來了!一個任務可以多次獲得對象的鎖。如果一個方法在同一個對象上調用了第二個方法,后者又調用了同一個對象上的另一個方法,就會發生這種情況。JVM負責跟蹤對象被加鎖的次數,如果一個對象被解鎖,計數變為0。在任務第一次給對象加鎖的時候,計數變為1。每當這個相同的任務在這個對象上獲得鎖,計數都會遞增。顯然,只有首先獲得了鎖的任務才能允許繼續獲取多個鎖。每當任務離開一個synchronized 方法,計數遞減,當計數為0的時候,鎖被完全釋放,其他任務可以使用此資源。

 

什么時候使用同步鎖呢?

  Brian同步規則:如果你正在寫一個變量,它可能接下來將被另一個線程讀取,或者正在讀取一個上一次已經被另一個線程寫過的變量,那么你必須使用同步,並且,讀寫線程都必須用相同的監視器鎖同步。

  注意:每個訪問臨界共享資源的方法都必須被同步,否則它們不會正確工作。
 

 如何使用同步鎖呢?

synchronized 關鍵字,它包括兩種用法:synchronized 方法和 synchronized 塊。 

  • synchronized 方法:
public synchronized void countNum(int n); 

  特定對象所有synchronized方法共享同一個鎖,這種機制確保了同一時刻對於每一個類實例,其所有聲明為 synchronized 的成員函數中至多只有一個處於可執行狀態(因為至多只有一個能夠獲得該類實例對應的鎖),從而有效避免了類成員變量的訪問沖突(只要所有可能訪問類成員變量的方法均被聲明為 synchronized)。 

  不光如此,靜態方法也可以聲明為 synchronized ,以控制其對類的靜態成員變量的訪問。

public static synchronized void countNum(int n); 

  synchronized 方法的缺陷:若將一個大的方法聲明為synchronized 將會大大影響效率。

  典型地,若將線程類的方法 run() 聲明為synchronized ,由於在線程的整個生命期內它一直在運行,因此將導致它對本類任何synchronized 方法的調用都永遠不會成功。當然我們可以通過將訪問類成員變量的代碼放到專門的方法中,將其聲明為synchronized ,並在主方法中調用來解決這一問題,但是 Java 為我們提供了更好的解決辦法,那就是 synchronized 塊。

 

  • synchronized 塊:
synchronized(SyncObject.Class) {  
    //允許訪問控制的代碼  
}  

  亦可寫成如下格式,this,指的就是當前這個類

synchronized(this) {  
    //允許訪問控制的代碼  
}  

  synchronized 塊是這樣一個代碼塊,其中的代碼必須獲得對象 syncObject (如前所述,可以是類實例或類)的鎖方能執行,具體機制同前所述。由於可以針對任意代碼塊,且可任意指定上鎖的對象,故靈活性較高。

  在使用synchronized 塊的時候,一定要遵循Brian同步規則,並對每個訪問臨界共享資源的方法都進行同步。

 

 

 

 

 


免責聲明!

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



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