首页>>后端>>java->Java多线程之synchronized锁的实现原理

Java多线程之synchronized锁的实现原理

时间:2023-11-29 本站 点击:52

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.Testminorversion:0majorversion:52flags:ACC_PUBLIC,ACC_SUPERConstantpool:#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:()Vflags:ACC_PUBLICCode:stack=1,locals=1,args_size=10:aload_01:invokespecial#1//Methodjava/lang/Object."<init>":()V4:returnLineNumberTable:line3:0LocalVariableTable:StartLengthSlotNameSignature050thisLcom/algorithms/interview/template/Test;publicstaticvoidmain(java.lang.String[]);descriptor:([Ljava/lang/String;)Vflags:ACC_PUBLIC,ACC_STATICCode:stack=2,locals=3,args_size=10:ldc#2//classcom/algorithms/interview/template/Test2:dup3:astore_14:monitorenter5:getstatic#3//Fieldjava/lang/System.out:Ljava/io/PrintStream;8:ldc#4//String锁10:invokevirtual#5//Methodjava/io/PrintStream.println:(Ljava/lang/String;)V13:aload_114:monitorexit15:goto2318:astore_219:aload_120:monitorexit21:aload_222:athrow23:returnExceptiontable:fromtotargettype51518any182118anyLineNumberTable:line6:0line7:5line8:13line9:23LocalVariableTable:StartLengthSlotNameSignature0240args[Ljava/lang/String;StackMapTable:number_of_entries=2frame_type=255/*full_frame*/offset_delta=18locals=[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_1JRT_ENTRY_NO_ASYNC(void,InterpreterRuntime::monitorenter(JavaThread*current,BasicObjectLock*elem))#ifdefASSERTcurrent->last_frame().interpreter_frame_verify_monitor(elem);#endifHandleh_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");#ifdefASSERTcurrent->last_frame().interpreter_frame_verify_monitor(elem);#endifJRT_ENDJRT_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中没有锁,则先将对象头MarkWord拷贝到当前线程的栈中锁记录的位置,通过CAS将把当前对象头MarkWord中锁记录的指针更新为当前线程栈中锁记录的位置;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);}//获取对象的markwordmarkWordmark=obj->mark();//如果if(mark.is_neutral()){//AnticipatesuccessfulCAS--theSTofthedisplacedmarkmust//bevisible<=theSTperformedbytheCAS.//替换所记录为当前markwordlock->set_displaced_header(mark);//然后通过CAS,将当前对象头中markword的所记录指针更新为锁记录的指针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)有一个清晰的认识。

作者:xjz1842&掘金著作权归作者所有。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/java/60.html