在Java中,最簡單粗暴的同步手段就是synchronized關鍵字,其同步的三種用法:
①.同步實例方法,鎖是當前實例對象
②.同步類方法,鎖是當前類對象
③.同步代碼塊,鎖是括號裏面的對象
示例:java
public class SynchronizedTest {
/**
* 同步實例方法,鎖實例對象
*/
public synchronized void test() {
}
/**
* 同步類方法,鎖類對象
*/
public synchronized static void test1() {
}
/**
* 同步代碼塊
*/
public void test2() {
// 鎖類對象
synchronized (SynchronizedTest.class) {
// 鎖實例對象
synchronized (this) {
}
}
}
}
複製代碼
javap -verbose查看上述示例:
編程
case Bytecodes::_monitorenter:
{
pop_object();
set_monitor_count(monitor_count() + 1);
break;
}
case Bytecodes::_monitorexit:
{
pop_object();
assert(monitor_count() > 0, "must be a monitor to exit from");
set_monitor_count(monitor_count() - 1);
break;
}
void pop_object() {
assert(is_reference(type_at_tos()), "must be reference type");
pop();
}
void pop() {
debug_only(set_type_at_tos(bottom_type()));
_stack_size--;
}
int monitor_count() const { return _monitor_count; }
void set_monitor_count(int mc) { _monitor_count = mc; }
複製代碼
從源碼中咱們發現當線程得到該對象鎖後,計數器就會加一,釋放鎖就會將計數器減一。安全
每一個對象都擁有本身的監視器,當這個對象由同步塊或者這個對象的同步方法調用時,執行方法的線程必須先獲取該對象的監視器才能進入同步塊和同步方法,若是沒有獲取到監視器的線程將會被阻塞在同步塊和同步方法的入口處,進入到BLOCKED狀態,如圖:數據結構
Monitor是線程私有的數據結構,每一個線程都有一個可用monitor record列表,同時 還有一個全局的可用列表。每個被鎖住的對象都會和一個monitor關聯(對象頭的MarkWord中的LockWord指向monitor的起始地址),同時monitor中有一個owner字段存放擁有該鎖的線程的惟一標識,表示該鎖被這個線程佔用。其結構以下: 多線程
Owner:初始時爲NULL表示當前沒有任何線程擁有該monitor record,當線程成功擁有該鎖後保存線程惟一標識,當鎖被釋放時又設置爲NULL
EntryQ:關聯一個系統互斥鎖(semaphore),阻塞全部試圖鎖住monitor record失敗的線程
RcThis:表示blocked或waiting在該monitor record上的全部線程的個數
Nest:用來實現重入鎖的計數
HashCode:保存從對象頭拷貝過來的HashCode值(可能還包含GC age)
Candidate:用來避免沒必要要的阻塞或等待線程喚醒,由於每一次只有一個線程可以成功擁有鎖,若是每次前一個釋放鎖的線程喚醒全部正在阻塞或等待的線程,會引發沒必要要的上下文切換(從阻塞到就緒而後由於競爭鎖失敗又被阻塞)從而致使性能嚴重降低。Candidate只有兩種可能的值0表示沒有須要喚醒的線程1表示要喚醒一個繼任線程來競爭鎖
併發
jdk1.6中synchronized的實現進行了各類優化,如適應性自旋、鎖消除、鎖粗化、輕量級鎖和偏向鎖,主要解決三種場景:
①.只有一個線程進入臨界區,偏向鎖
②.多線程未競爭,輕量級鎖
③.多線程競爭,重量級鎖
偏向鎖→輕量級鎖→重量級鎖過程,鎖能夠升級但不能降級,這種策略是爲了提升得到鎖和釋放鎖的效率,源碼解析能夠看佔小狼——synchronized實現性能
引入偏向鎖的目的是:在沒有多線程競爭的狀況下,儘可能減小沒必要要的輕量級鎖執行路徑。相對於輕量級鎖,偏向鎖只依賴一次CAS原子指令置換ThreadID,不過一旦出現多個線程競爭時必須撤銷偏向鎖,主要校驗是否爲偏向鎖、鎖標識位以及ThreadID。
優化
引入輕量級鎖的主要目的是在多線程沒有競爭的前提下,減小傳統的重量級鎖使用操做系統互斥量產生的性能消耗。若是多個線程在同一時刻進入臨界區,會致使輕量級鎖膨脹升級重量級鎖,因此輕量級鎖的出現並不是是要替代重量級鎖,在有多線程競爭的狀況下,輕量級鎖比重量級鎖更慢。this
①.若是對象的Mark Word仍然指向着線程的鎖記錄,執行②
②.用CAS操做把對象當前的Mark Word和線程中複製的Displaced Mark Word替換回來,若是成功,則說明釋放鎖成功,不然執行③
③.若是CAS操做替換失敗,說明有其餘線程嘗試獲取該鎖,則須要在釋放鎖的同時須要喚醒被掛起的線程
spa
重量級鎖經過對象內部的監視器(monitor)實現,其中monitor的本質是依賴於底層操做系統的Mutex Lock實現,操做系統實現線程之間的切換須要從用戶態到內核態的切換,切換成本很是高。
芋道源碼——synchronized實現原理 《深刻理解Java虛擬機》 《java併發編程的藝術》