本文主要就Hollis深入理解Java多線程系列文章的總結(jié)。原文詳見[HollisChuang's Blog邑飒,歡迎大家關(guān)注挽铁!
Synchronized實(shí)現(xiàn)原理
public class SynchronizedTest {
public synchronized void doSth(){
System.out.println("Hello World");
}
public void doSth1(){
synchronized (SynchronizedTest.class){
System.out.println("Hello World");
}
}
}
同步代碼塊使用monitorenter和monitorexit兩個指令實(shí)現(xiàn)。執(zhí)行monitorenter指令加鎖,執(zhí)行monitorexit指令釋放鎖矢空。
同步方法使用ACC_SYNCHRONIZED關(guān)鍵字隱式的對方法加鎖,當(dāng)線程執(zhí)行的方法被標(biāo)上ACC_SYNCHRONIZED時,需要先獲得鎖才能執(zhí)行該方法窜骄。
每個對象維護(hù)一個計(jì)數(shù)器,記錄對象被鎖次數(shù)摆屯,當(dāng)一個線程獲得鎖時邻遏,該計(jì)數(shù)器自增1,當(dāng)同一個線程釋放鎖是虐骑,該計(jì)數(shù)器減1准验。
Java對象模型
每一個Java類在被JVM加載的時候,JVM會為這個類創(chuàng)建一個instanceKlass富弦,保存在方法區(qū)沟娱,用于在JVM層表示該Java類。當(dāng)我們使用new創(chuàng)建一個對象時腕柜,JVM會創(chuàng)建一個instanceOopDesc對象济似,包含兩部分信息:對象頭及元數(shù)據(jù)。對象頭中有一些運(yùn)行時數(shù)據(jù)盏缤,其中包括多線程相關(guān)鎖的信息砰蠢。元數(shù)據(jù)維護(hù)的指針指向?qū)ο笏鶎兕惖?strong>instanceKlass。
Java對象頭
class oopDesc {
friend class VMStructs;
private:
volatile markOop _mark;
union _metadata {
wideKlassOop _klass;
narrowOop _compressed_klass;
} _metadata;
}
markword設(shè)計(jì)是將存儲空間劃分為多個比特位唉铜,并在不同對象狀態(tài)下賦予比特位含義台舱。下圖為32為虛擬機(jī)
Monitor實(shí)現(xiàn)原理
同步方法和同步代碼塊都是基于monitor實(shí)現(xiàn)的。
操作系統(tǒng)中管程
管程是一種程序結(jié)構(gòu)潭流,結(jié)構(gòu)內(nèi)多個多個子程序(對象或模塊)形成多個共享線程互斥訪問共享資源竞惋。這些共享資源一般是硬件資源或者共享變量。管程實(shí)現(xiàn)了在一個時間點(diǎn)最多只有一個線程執(zhí)行管程中某個子程序灰嫉。
Java線程同步相關(guān)之Monitor
對象的所有方法互斥執(zhí)行拆宛,一個monitor只有一個運(yùn)行許可,任一線程進(jìn)入任何方法都需要獲得這個許可讼撒,離開時歸還許可浑厚。提供singal機(jī)制股耽,允許正在持有許可的線程放棄許可,等待某個條件成立后當(dāng)前線程可以通知等待這個條件變量的線程去重新獲取許可钳幅。
Monitor實(shí)現(xiàn)
Java虛擬機(jī)(HotSpot)的monitor是基于C++實(shí)現(xiàn)的物蝙,主要數(shù)據(jù)結(jié)構(gòu)如下
ObjectMonitor() {
_header = NULL;
_count = 0;
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL;
_WaitSet = NULL;
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ;
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}
關(guān)鍵屬性如下:
_owner // 指向持有ObjectMonitor對象的線程
_WaitSet // 存放處于wait狀態(tài)的線程隊(duì)列
_EntryList // 存在處于wait狀態(tài)的線程隊(duì)列
_recursion // 鎖的重入次數(shù)
_count // 用來記錄該線程獲取鎖的次數(shù)
當(dāng)多個線程同時訪問一段同步代碼時,會首先進(jìn)入EntryList 隊(duì)列中敢艰,當(dāng)某個線程獲取到對象的monitor后進(jìn)入Owner 區(qū)域并把ower 變量設(shè)置為當(dāng)前線程诬乞,同時monitor中計(jì)數(shù)器加1。即線程獲得鎖盖矫。
當(dāng)持有monitor的線程調(diào)用wait() 方法丽惭,將釋放當(dāng)前持有monitor,owner變量恢復(fù)為null辈双,count減1责掏,同時WaitSet集合中等待線程會被喚醒。當(dāng)前線程執(zhí)行完畢也會釋放monitor湃望。如下圖所示换衬。
獲取鎖
void ATTR ObjectMonitor::enter(TRAPS) {
Thread * const Self = THREAD ;
void * cur ;
//通過CAS嘗試把monitor的`_owner`字段設(shè)置為當(dāng)前線程
cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;
//獲取鎖失敗
if (cur == NULL) {assert (_recursions == 0, "invariant") ;
assert (_owner == Self, "invariant") ;
// CONSIDER: set or assert OwnerIsThread == 1
return ;
}
// 如果舊值和當(dāng)前線程一樣,說明當(dāng)前線程已經(jīng)持有鎖证芭,此次為重入瞳浦,_recursions自增,并獲得鎖废士。
if (cur == Self) {
// TODO-FIXME: check for integer overflow! BUGID 6557169.
_recursions ++ ;
return ;
}
// 如果當(dāng)前線程是第一次進(jìn)入該monitor叫潦,設(shè)置_recursions為1,_owner為當(dāng)前線程
if (Self->is_lock_owned ((address)cur)) {
assert (_recursions == 0, "internal state error");
_recursions = 1 ;
// Commute owner from a thread-specific on-stack BasicLockObject address to
// a full-fledged "Thread *".
_owner = Self ;
OwnerIsThread = 1 ;
return ;
}
// 省略部分代碼官硝。
// 通過自旋執(zhí)行ObjectMonitor::EnterI方法等待鎖的釋放
for (;;) {
jt->set_suspend_equivalent();
// cleared by handle_special_suspend_equivalent_condition()
// or java_suspend_self()
EnterI (THREAD) ;
if (!ExitSuspendEquivalent(jt)) break ;
//
// We have acquired the contended monitor, but while we were
// waiting another thread suspended us. We don't want to enter
// the monitor while suspended because that would surprise the
// thread that suspended us.
//
_recursions = 0 ;
_succ = NULL ;
exit (Self) ;
jt->java_suspend_self();
}
}
釋放鎖
void ATTR ObjectMonitor::exit(TRAPS) {
Thread * Self = THREAD ;
//如果當(dāng)前線程不是Monitor的所有者
if (THREAD != _owner) {
if (THREAD->is_lock_owned((address) _owner)) { //
// Transmute _owner from a BasicLock pointer to a Thread address.
// We don't need to hold _mutex for this transition.
// Non-null to Non-null is safe as long as all readers can
// tolerate either flavor.
assert (_recursions == 0, "invariant") ;
_owner = THREAD ;
_recursions = 0 ;
OwnerIsThread = 1 ;
} else {
// NOTE: we need to handle unbalanced monitor enter/exit
// in native code by throwing an exception.
// TODO: Throw an IllegalMonitorStateException ?
TEVENT (Exit - Throw IMSX) ;
assert(false, "Non-balanced monitor enter/exit!");
if (false) {
THROW(vmSymbols::java_lang_IllegalMonitorStateException());
}
return;
}
}
// 如果_recursions次數(shù)不為0.自減
if (_recursions != 0) {
_recursions--; // this is simple recursive enter
TEVENT (Inflated exit - recursive) ;
return ;
}
//省略部分代碼矗蕊,根據(jù)不同的策略(由QMode指定),從cxq或EntryList中獲取頭節(jié)點(diǎn)氢架,通過ObjectMonitor::ExitEpilog方法喚醒該節(jié)點(diǎn)封裝的線程傻咖,喚醒操作最終由unpark完成。
注意 sychronized操作是重量級操作岖研,需要將用戶態(tài)轉(zhuǎn)換到內(nèi)核態(tài)卿操。
Java虛擬機(jī)鎖優(yōu)化技術(shù)
線程狀態(tài)
分為以下五種,分別為:初始狀態(tài)(New)孙援,就緒狀態(tài)(Runnable)害淤,運(yùn)行狀態(tài)(Running),阻塞狀態(tài)(Blocked)拓售,死亡狀態(tài)(Dead)窥摄。各種狀態(tài)間轉(zhuǎn)換如下圖所示:
自旋鎖
線程不放棄處理器執(zhí)行時間,等待共享資源可訪問后繼續(xù)執(zhí)行邻辉,自旋鎖只是將當(dāng)前線程不停執(zhí)行循環(huán)體并檢查共享資源溪王,不改變線程狀態(tài)。
注意:線程數(shù)不停增加是導(dǎo)致性能下降值骇。
鎖消除
在使用synchronized時莹菱,如果使用JIT逃逸分析發(fā)現(xiàn)并無線程安全問題,則會使用鎖消除吱瘩。