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

 

引言

   最近一段時間,實驗室已經傾巢出動找實習了,博主也湊合了一把,結果有悲有喜,BAT理所應當的跪了,也收到了其餘的offer,總的感覺是有必要夯實基礎啊。
  言歸正傳,最近在看到java多線程的時候,發現線程不少都是用了synchronized(同步鎖)的關鍵字,對它的瞭解還只停留在「鎖」的概念上。博主也在網上搜了一些介紹synchronized 的博客,可是越看越糊塗,並且個人水平尚未到可以鑑別真僞,因此爲了不「練錯神功,走火入魔「,我捧起了《Thinking in Java》。本文就是針對此書關於synchronized 的內容,加入了一些本身的代碼驗證,不求可以得心應手,只能力求不出錯,不誤導你們,若是有些地方存在問題,也請你們海涵,指出意見。 
 

爲何要使用同步鎖?

   在《Thinking in Java》中,是這麼說的:對於併發工做,你須要某種方式來防止兩個任務訪問相同的資源(其實就是共享資源競爭)。  防止這種衝突的方法就是當資源被一個任務使用時,在其上加鎖。第一個訪問某項資源的任務必須鎖定這項資源,使其餘任務在其被解鎖以前,就沒法訪問它了,而在其被解鎖之時,另外一個任務就能夠鎖定並使用它了。
 
   基本上全部的併發模式在解決線程衝突問題的時候,都是採用序列化訪問共享資源的方案。這意味在給定時刻只容許一個任務訪問共享資源,一般這是經過在代碼前面加上一條鎖語句來實現的,鎖語句產生了一種互相排斥的效果,這種機制稱爲互斥量(mutex)。
 

同步鎖的實現原理?

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

   重點來了!一個任務能夠屢次得到對象的鎖。若是一個方法在同一個對象上調用了第二個方法,後者又調用了同一個對象上的另外一個方法,就會發生這種狀況。JVM負責跟蹤對象被加鎖的次數,若是一個對象被解鎖,計數變爲0。在任務第一次給對象加鎖的時候,計數變爲1。每當這個相同的任務在這個對象上得到鎖,計數都會遞增。顯然,只有首先得到了鎖的任務才能容許繼續獲取多個鎖。每當任務離開一個synchronized 方法,計數遞減,當計數爲0的時候,鎖被徹底釋放,其餘任務可使用此資源。java

 

何時使用同步鎖呢?

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

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

 如何使用同步鎖呢?

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

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

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

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

public static synchronized void countNum(int n); 

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

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

 

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

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

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

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

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

相關文章
相關標籤/搜索