引言
為什么要使用同步鎖?
同步鎖的實現原理?
重點來了!一個任務可以多次獲得對象的鎖。如果一個方法在同一個對象上調用了第二個方法,后者又調用了同一個對象上的另一個方法,就會發生這種情況。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同步規則,並對每個訪問臨界共享資源的方法都進行同步。