当前位置: 首页 > 后端技术 > Java

同步原理分析

时间:2023-04-01 14:45:38 Java

Synchronized原理分析Synchronized是Java提供的一种同步源语言。它为共享资源提供原子性和可见性保证。本文通过原子性和可见性两个维度分析其实现原理。如下实现monitorenter和monitorexit或ACC_SYNCHRONIZED的加锁加锁流程。锁升级过程new时,判断是否开启偏向锁开启偏向锁,构建匿名偏向锁(101)关闭偏向锁,构建无锁对象(001)无锁(001)时一个线程被加锁,直接加自旋锁/轻量级锁(00)当一个线程被偏向锁加锁时,锁状态不变,保存线程ID。偏向锁遇到多个线程交替加锁,当线程运行到安全点时,取消偏向锁,升级为自旋锁/轻量级锁(00)。自旋锁是让每个线程通过CAS指令更新对象头中的标记。如果自旋等待时间过长,则锁扩展为重量级锁(10)重量级锁由ObjectMonitor实现,需要从用户态切换到内核态。当竞争不激烈时,重量级锁自动降级为轻量级锁monitorenter源码分析CASE(_monitorenter):{//获取锁对象ooplockee=STACK_OBJECT(-1);//在线程栈上找到一个空闲的BasicObjectLock对象BasicObjectLock*limit=isate->monitor_base();BasicObjectLock*most_recent=(BasicObjectLock*)isstate->stack_base();BasicObjectLock*条目=NULL;while(most_recent!=limit){if(most_recent->obj()==NULL)entry=most_recent;elseif(most_recent->obj()==lockee)break;最近的++;}如果(条目!=NULL){//保存锁对象,表示当前BasicObjectLock持有锁对象lockeeentry->set_obj(lockee);诠释成功=假;uintptr_tepoch_mask_in_place=(uintptr_t)markOopDesc::epoch_mask_in_place;markOopmark=lockee->mark();//获取锁对象的头部标记信息//获取没有哈希值的标记位值,这里为0intptr_thash=(intptr_t)markOopDesc::no_hash;//判断使用偏向锁if(mark->has_bias_pattern()){uintptr_tthread_ident;uintptr_t预期的_bias_locking_value;thread_ident=(uintptr_t)istate->thread();//获取线程IDexpected_bias_locking_value=(((uintptr_t)lockee->klass()->prototype_header()|thread_ident)^(uint)ptr_t)&~((uintptr_t)markOopDesc::age_mask_in_place);/*expected_bias_locking_value为0,说明偏向锁没有被批量撤销,当前线程持有偏向锁,直接退出*/if(anticipated_bias_locking_value==0){//已经偏向th是线程,无事可做if(PrintBiasedLockingStatistics){(*BiasedLocking::biased_lock_entry_count_addr())++;成功=真;}elseif((anticipated_bias_locking_value&markOopDesc::biased_lock_mask_in_place)!=0lock){/*as_anticipated_0,可能是批量取消偏向锁。需要继续判断是否有线程持有偏向锁。如果其他线程持有偏向锁,判断发生冲突,需要取消偏向锁*/markOopheader=lockee->klass()->prototype_header();if(hash!=markOopDesc::no_hash){header=header->copy_set_hash(hash);}//CAS将对象头从mark替换为header,取消偏向锁}}否则如果((反cipated_bias_locking_value&epoch_mask_in_place)!=0){/*如果expected_bias_locking_value不为0,则批量取消偏向锁时需要改变epoch的值。如果epoch改变,当前线程需要重新偏置*/markOopnew_header=(markOop)((intptr_t)lockee->klass()->prototype_header()|thread_ident);if(hash!=markOopDesc::no_hash){new_header=new_header->copy_set_hash(hash);}//CAS重新偏置if(lockee->cas_set_mark(new_header,mark)==mark){if(PrintBiasedLockingStatistics)(*BiasedLocking::rebiased_lock_entry_count_addr())++;}else{//CAS失败,发生竞争,然后进入monitorenterCALL_VM(InterpreterRuntime::monitorenter(THREAD,entry),handle_exception);成功=真;}else{/*以上条件都不满足,说明开启了偏置锁,此时偏向锁状态为匿名偏向,尝试CAS将其偏向当前线程*/)markOopDesc::age_mask_in_place|epoch_mask_in_place));if(hash!=markOopDesc::no_hash){header=header->copy_set_hash(hash);}markOopnew_header=(markOop)((uintptr_t)header|thread_ident);//CAS重新偏置if(lockee->cas_set_mark(new_header,header)==header){if(PrintBiasedLockingStatistics)(*BiasedLocking::anonymously_biased_lock_entry_count_addr())++;}else{//CAS失败,发生竞争,然后进入monitorenterCALL_VM(InterpreterRuntime::monitorenter(THREAD,entry),handle_exception);成功=真;}}//如果没有获取到锁,进入传统的轻量级锁if(!success){markOopdisplaced=lockee->mark()->set_unlocked();entry->lock()->set_displaced_header(displaced);boolcall_vm=UseHeavyMonitors;//判断是否直接使用重量级锁/*如果不指定直接使用重量级锁,则尝试通过CAS操作获取轻量级锁,即替换头指针指向entry*/if(call_vm||lockee->cas_set_mark((markOop)entry,displaced)!=displaced){//如果失败,可能是当前线程的轻量级锁重入,则判断是否是锁重入if(!call_vm&&THREAD->is_lock_owned((address)displaced->clear_lock_bits())){//轻量级锁可重入,无需设置displaced_header信息entry->lock()->set_displaced_header(NULL);}else{//否则调用monitorenterCALL_VM(InterpreterRuntime::monitorenter(THREAD,entry),handle_exc节);}}}UPDATE_PC_AND_TOS_AND_CONTINUE(1,-1);}else{//如果没有找到,设置more_monitors标志,解释器分配一个新的BasicObjectLock并重试isstate->set_msg(more_monitors);UPDATE_PC_AND_RETURN(0);//重新执行}}sync可见性sync通过缓存一致性协议保证可见性MESIM(modified):modifiedE(exclusive):exclusiveS(shared):sharedI(invalid):invalidsync和Lock的区别使用syncAutomatic加锁和解锁,Lock需要手动加锁和解锁功能Lock支持不同条件(不同的等待队列),指定唤醒Lock可以使用tryLock支持timeoutsynclock不支持timeoutLock可以使用Lock.lockInterruptibly来响应未获得的中断同步锁的线程处于Blocked状态,无法响应中断。Lock支持公平锁和非公平锁。sync只支持非公平锁原则。无锁乐观锁、偏向锁、轻量级锁都是CAS实现的用户态轻量级锁,乐观锁、Monitor实现的重量级锁都是悲观锁