重點來了!一個任務能夠屢次得到對象的鎖。若是一個方法在同一個對象上調用了第二個方法,後者又調用了同一個對象上的另外一個方法,就會發生這種狀況。JVM負責跟蹤對象被加鎖的次數,若是一個對象被解鎖,計數變爲0。在任務第一次給對象加鎖的時候,計數變爲1。每當這個相同的任務在這個對象上得到鎖,計數都會遞增。顯然,只有首先得到了鎖的任務才能容許繼續獲取多個鎖。每當任務離開一個synchronized 方法,計數遞減,當計數爲0的時候,鎖被徹底釋放,其餘任務可使用此資源。java
Brian同步規則:若是你正在寫一個變量,它可能接下來將被另外一個線程讀取,或者正在讀取一個上一次已經被另外一個線程寫過的變量,那麼你必須使用同步,而且,讀寫線程都必須用相同的監視器鎖同步。多線程
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(SyncObject.Class) { //容許訪問控制的代碼 }
亦可寫成以下格式,this,指的就是當前這個類線程
synchronized(this) { //容許訪問控制的代碼 }
synchronized 塊是這樣一個代碼塊,其中的代碼必須得到對象 syncObject (如前所述,能夠是類實例或類)的鎖方能執行,具體機制同前所述。因爲能夠針對任意代碼塊,且可任意指定上鎖的對象,故靈活性較高。對象
在使用synchronized 塊的時候,必定要遵循Brian同步規則,並對每一個訪問臨界共享資源的方法都進行同步。blog