1. 程式人生 > 其它 >java多執行緒之synchronized鎖的實現原理

java多執行緒之synchronized鎖的實現原理

java中synchronized是由JVM層實現的鎖,而Lock的實現完全是由JAVA層實現,jdk 1.5之前synchronized是重量級鎖實現,效率比較低,經過JVM·的優化,jdk1.5版本及後synchronbized的鎖效率已經和Lock差不多。

synchronized是通過monitorEnter和monitorExit的JVM指令實現的,

下面通過一個簡單demo展示下

publicstaticvoidmain(String[]args){
synchronized(Test.class){
System.out.println("鎖");
}
}

上面的demo的位元組碼如下,可以看到synchronized關鍵字的括號包含的程式碼鎖對應的位元組碼開始前嵌入monitorenter,末尾嵌入monitorexit指令。

publicclasscom.algorithms.interview.template.Test
minorversion:0
majorversion:52
flags:ACC_PUBLIC,ACC_SUPER
Constantpool:
#1=Methodref#6.#24//java/lang/Object."<init>":()V
#2=Class#25//com/algorithms/interview/template/Test
#3=Fieldref#26.#27//java/lang/System.out:Ljava/io/PrintStream;
#4=String#28//鎖
#5=Methodref#29.#30//java/io/PrintStream.println:(Ljava/lang/String;)V
#6=Class#31//java/lang/Object
#7=Utf8<init>
#8=Utf8()V
#9=Utf8Code
#10=Utf8LineNumberTable
#11=Utf8LocalVariableTable
#12=Utf8this
#13=Utf8Lcom/algorithms/interview/template/Test;
#14=Utf8main
#15=Utf8([Ljava/lang/String;)V
#16=Utf8args
#17=Utf8[Ljava/lang/String;
#18=Utf8StackMapTable
#19=Class#17//"[Ljava/lang/String;"
#20=Class#31//java/lang/Object
#21=Class#32//java/lang/Throwable
#22=Utf8SourceFile
#23=Utf8Test.java
#24=NameAndType#7:#8//"<init>":()V
#25=Utf8com/algorithms/interview/template/Test
#26=Class#33//java/lang/System
#27=NameAndType#34:#35//out:Ljava/io/PrintStream;
#28=Utf8鎖
#29=Class#36//java/io/PrintStream
#30=NameAndType#37:#38//println:(Ljava/lang/String;)V
#31=Utf8java/lang/Object
#32=Utf8java/lang/Throwable
#33=Utf8java/lang/System
#34=Utf8out
#35=Utf8Ljava/io/PrintStream;
#36=Utf8java/io/PrintStream
#37=Utf8println
#38=Utf8(Ljava/lang/String;)V
{
publiccom.algorithms.interview.template.Test();
descriptor:()V
flags:ACC_PUBLIC
Code:
stack=1,locals=1,args_size=1
0:aload_0
1:invokespecial#1//Methodjava/lang/Object."<init>":()V
4:return
LineNumberTable:
line3:0
LocalVariableTable:
StartLengthSlotNameSignature
050thisLcom/algorithms/interview/template/Test;
publicstaticvoidmain(java.lang.String[]);
descriptor:([Ljava/lang/String;)V
flags:ACC_PUBLIC,ACC_STATIC
Code:
stack=2,locals=3,args_size=1
0:ldc#2//classcom/algorithms/interview/template/Test
2:dup
3:astore_1
4:monitorenter
5:getstatic#3//Fieldjava/lang/System.out:Ljava/io/PrintStream;
8:ldc#4//String鎖
10:invokevirtual#5//Methodjava/io/PrintStream.println:(Ljava/lang/String;)V
13:aload_1
14:monitorexit
15:goto23
18:astore_2
19:aload_1
20:monitorexit
21:aload_2
22:athrow
23:return
Exceptiontable:
fromtotargettype
51518any
182118any
LineNumberTable:
line6:0
line7:5
line8:13
line9:23
LocalVariableTable:
StartLengthSlotNameSignature
0240args[Ljava/lang/String;
StackMapTable:number_of_entries=2
frame_type=255/*full_frame*/
offset_delta=18
locals=[class"[Ljava/lang/String;",classjava/lang/Object]
stack=[classjava/lang/Throwable]
frame_type=250/*chop*/
offset_delta=4
}

那麼monitorenter和monitorexit指令是如何作用的呢想要弄清楚這個問題,首先還是得了解Java得物件頭(基於64位作業系統)

|MardWord(64bits)|鎖狀態|
|unused25|identiy_hashcode:31|unused1|age4|biase_lock0|01|無鎖|
|thradId:54|epoch:2|unused:1|age:4|biase_lock1|01|偏向鎖|
|ptr_to_lock_record62|00|輕量級鎖|
|ptr_to_heavyweight_lock62|10|輕量級鎖|
|空62|11|標記GC|

其中物件頭中MarkWord中鎖標誌位就是表示物件鎖的狀態的,如上圖所示

JVM的原始碼是如果處理synchronized鎖

自從jdk1.5之後,JVM就對synchronized進行了優化,那麼它是如何優化成現在 幾乎和ReentrantLock效能差不多的呢,那麼加下來往下看' 下面是monitorenter和monitorexit指令的經過編譯器執行對應的程式碼的位置 src/hotspot/share/interpreter/interpreterRuntime.cpp

//%notemonitor_1
JRT_ENTRY_NO_ASYNC(void,InterpreterRuntime::monitorenter(JavaThread*current,BasicObjectLock*elem))
#ifdefASSERT
current->last_frame().interpreter_frame_verify_monitor(elem);
#endif
Handleh_obj(current,elem->obj());
assert(Universe::heap()->is_in_or_null(h_obj()),
"mustbeNULLoranobject");
ObjectSynchronizer::enter(h_obj,elem->lock(),current);
assert(Universe::heap()->is_in_or_null(elem->obj()),
"mustbeNULLoranobject");
#ifdefASSERT
current->last_frame().interpreter_frame_verify_monitor(elem);
#endif
JRT_END
JRT_LEAF(void,InterpreterRuntime::monitorexit(BasicObjectLock*elem))
oopobj=elem->obj();
assert(Universe::heap()->is_in(obj),"mustbeanobject");
//TheobjectcouldbecomeunlockedthroughaJNIcall,whichwehavenootherchecksfor.
//GiveafatalmessageifCheckJNICalls.Otherwiseweignoreit.
if(obj->is_unlocked()){
if(CheckJNICalls){
fatal("ObjecthasbeenunlockedbyJNI");
}
return;
}
ObjectSynchronizer::exit(obj,elem->lock(),JavaThread::current());
//Freeentry.Ifitisnotcleared,theexceptionhandlingcodewilltrytounlockthemonitor
//againatmethodexitorinthecaseofanexception.
elem->set_obj(NULL);
JRT_END

從上面知道monitorenter指令是呼叫ObjectSynchronizer::enter加鎖,對應原始碼位置是src/hotspot/share/runtime/synchronizer.cpp 首先主要看下enter的加鎖的方法
1.判斷是否開啟偏向鎖,handle_sync_on_value_based_class同步處理偏向鎖處理
2.如果物件頭中MarkWord中沒有鎖,則先將物件頭寫入當前執行緒的棧中,通過CAS將把 MarkWord中對應位置更新為header的指標
3.如果物件頭中markword已經存在鎖,則通過inflate進行膨脹,嘗試獲取重量級鎖, (即使用ObjectMonitor進行加鎖)

//-----------------------------------------------------------------------------
//MonitorEnter/Exit
//Theinterpreterandcompilerassemblycodetriestolockusingthefastpath
//ofthisalgorithm.Makesuretoupdatethatcodeifthefollowingfunctionis
//changed.Theimplementationisextremelysensitivetoracecondition.Becareful.
voidObjectSynchronizer::enter(Handleobj,BasicLock*lock,JavaThread*current){
//是否開啟偏向鎖
if(obj->klass()->is_value_based()){
handle_sync_on_value_based_class(obj,current);
}
//獲取物件的markword
markWordmark=obj->mark();
//如果
if(mark.is_neutral()){
//AnticipatesuccessfulCAS--theSTofthedisplacedmarkmust
//bevisible<=theSTperformedbytheCAS.
lock->set_displaced_header(mark);
if(mark==obj()->cas_set_mark(markWord::from_pointer(lock),mark)){
return;
}
//Fallthroughtoinflate()...
}elseif(mark.has_locker()&&
current->is_lock_owned((address)mark.locker())){
assert(lock!=mark.locker(),"mustnotre-lockthesamelock");
assert(lock!=(BasicLock*)obj->mark().value(),"don'trelockwithsameBasicLock");
lock->set_displaced_header(markWord::from_pointer(NULL));
return;
}
//Theobjectheaderwillneverbedisplacedtothislock,
//soitdoesnotmatterwhatthevalueis,exceptthatit
//mustbenon-zerotoavoidlookinglikeare-entrantlock,
//andmustnotlooklockedeither.
lock->set_displaced_header(markWord::unused_mark());
//Anasyncdeflationcanraceaftertheinflate()callandbefore
//enter()canmaketheObjectMonitorbusy.enter()returnsfalseif
//wehavelosttheracetoasyncdeflationandwesimplytryagain.
while(true){
ObjectMonitor*monitor=inflate(current,obj(),inflate_cause_monitor_enter);
if(monitor->enter(current)){
return;
}
}
}

總結
今天主要對Java中synchronized關鍵字的底層原理的分析,對於jdk的對於它的鎖優化原理, 由無鎖-->偏向鎖-->輕量級鎖(CAS)-->重量級鎖(OjectMonitor)有一個清晰的認識。