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)有一個清晰的認識。