java同步锁synchronized用法,java synchronized实现原理
目录
1同步场景回顾2反汇编寻找锁实现原理3同步虚拟机源3.1热点源监视器生成3.2热点源监视器竞争3.3热点源监视器等待3.4热点源监视器发布
00-1010目标:synchronized Review(锁分类——多线程)概念synchronized:是Java中的一个关键字,也是同步锁的一种。Java锁分为以下几个小技巧:乐观锁、悲观锁(syn)互斥锁(syn)、共享锁公平锁、不公平锁(syn)互斥锁(syn)、读写锁重入锁(syn)分段锁同步JDK1.6锁升级(无锁偏向锁(non-lock)-轻量级锁-重量级锁(lock)大家肯定会想到多线程(并发)。接下来,我们简单回顾一下多线程的特点。多线程特性回顾(面试常见问题)原子性:指一个操作或多个操作,都被执行并且执行的过程不会被任何因素打断,或者都不被执行。可见性:是指当多个线程访问一个资源时,该资源的状态和值信息对其他线程是可见的。有序性:指程序中代码的执行顺序(编译器会重新安排)
原子实现审查保证原子性?
com . syn.com . syn . th . syncatomicity
包com . syn.com . syn . th;导入Java . util . concurrent . time unit;/* Target 3360测试原子性问题1。调用正常(未锁定)方法;两个线程都可以正常执行。2.调用锁方法,只有一个线程可以正常执行,其他线程排队等待*/public类syncatomicity { public static void main(string[]args)抛出中断异常{ syncatomicity syncatomicity=new syncatomicity();//同步修饰实例方法//new thread(()-sync atomicity . test sync())。start();//新线程(()-syncAtomicity.testSYNC())。start();//同步修改静态方法new thread(()-sync atomicity . testsyncforstatic())。start();新线程(()-sync atomicity . testsyncforstatic())。start();//普通方法//new thread(()-sync atomicity . test())。start();//新线程(()- syncAtomicity.test())。start();}//锁定方法public synchronized void test sync(){ system . out . println( enter test sync方法);Try {//模拟方法体尚未执行完TimeUnit。睡眠时间(1);} catch(interrupted exception e){ e . printstacktrace();} }//锁定方法公共同步静态void testSYNCforstatic(){ system . out . println( enter test sync method );Try {//模拟方法体尚未执行完TimeUnit。睡眠时间(1);} catch(interrupted exception e){ e . printstacktrace();} }//normal method publicviddest(){ system . out . println(输入测试方法);Try {//模拟方法体尚未执行完t
imeUnit.HOURS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } }}总结我们发现在同一时刻确实只有一个线程进入,保证了原子性这是什么原理呢?
2 反汇编寻找锁实现原理
目标 通过javap反汇编看一下synchronized到底是怎么加锁的
com.syn.BTest
public class BTest { private static Object object = new Object(); public synchronized void testMethod() { System.out.println("Hello World -synchronized method "); } public static void main(String[] args) { synchronized (object) { System.out.println("Hello World -synchronized block "); } }}
反汇编后,我们将看到什么?
JDK自带的一个工具: javap ,对字节码进行反汇编:
//com.syn.BTestjavap -v -c BTest.class
反汇编后
解释被synchronized修饰的代码块,多了两个指令monitorenter、monitorexit即JVM使用monitorenter和monitorexit两个指令实现同步
解释被synchronized修饰的方法;增加 了ACC_SYNCHRONIZED 修饰。会隐式调用monitorenter和monitorexit。monitorenter原理(重要)monitorenter首先我们来看一下JVM规范中对于monitorenter的描述
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.monitorenter
翻译如下:每一个对象都会和一个监视器monitor关联。监视器被占用时会被锁住,其他线程无法来获取该monitor。当JVM执行某个线程的某个方法内部的monitorenter时,它会尝试去获取当前对象对应的monitor的所有权。其过程如下:
若monior的进入数为0,线程可以进入monitor,并将monitor的进入数置为1。当前线程成为monitor的owner(所有者)若线程已拥有monitor的所有权,允许它重入monitor,则进入monitor的进入数加1若其他线程已经占有monitor的所有权,那么当前尝试获取monitor的所有权的线程会被阻塞,直到monitor的进入数变为0,才能重新尝试获取monitor的所有权。monitorexit(重要)能执行monitorexit指令的线程一定是拥有当前对象的monitor的所有权的线程。执行monitorexit时会将monitor的进入数减1。当monitor的进入数减为0时,当前线程退出monitor,不再拥有monitor的所有权,此时其他被这个monitor阻塞的线程可以尝试去获取这个
monitor的所有权
monitorexit释放锁。
monitorexit插入在方法结束处和异常处,JVM保证每个monitorenter必须有对应的monitorexit。
tips(重要)
关于monitorenter和monitorexit描述上面文字太多,杜绝去念!!!!!!用图说话!!!! !!!!!!!!
类:com.syn.BTest
public static void main(String[] args) { synchronized (object) { System.out.println("Hello World -synchronized block "); } }
总结:通过上面的流程我们发现1、synchronized是靠Monitor关联拿到锁的2、如果竞争的时候拿不到锁,线程就去竞争队列3、如果拿到锁了,第二次拿,它又拿到锁,其他线程进入阻塞队列4、如果拿到锁的线程调用了wait方法,其他线程进入等待队列5、释放锁,需要将计数器减减操作6、出现异常,也释放锁。
3 synchronized虚拟机源码
synchronized是Java中的关键字,无法通过JDK源码查看它的实现,它是由JVM提供支持的,所以如果想要了解具体的实现需要查看JVM源码
目标:JVM虚拟机源码下载
http://hg.openjdk.java.net/jdk8/jdk8/hotspot/或者http://hg.openjdk.java.net/jdk8/jdk8/hotspot/archive/tip.zip
解压查看即可,无需环境搭建
3.1 HotSpot源码Monitor生成
目标: 通过JVM虚拟机源码分析synchronized监视器Monitor是怎么生成的tips:c++源码只看重点、弄懂原理c++重要吗?不重要但是面试时很重要,面试过去了就不重要!!!!!!!!!!!!学别人不会的东西你才有价值!!!!你会、大家都会,没啥意思!!在HotSpot虚拟机中,monitor监视器是由ObjectMonitor实现的。构造器代码src/share/vm/runtime/objectMonitor.hpphpp可以include包含cpp的东西,两者都是c++的代码
//构造器ObjectMonitor() {_header = NULL;_count = 0;_waiters = 0,_recursions = 0; // 递归:线程的重入次数,典型的System.out.println_object = NULL; // 对应synchronized (object)对应里面的object_owner = NULL; // 标识拥有该monitor的线程_WaitSet = NULL; // 因为调用object.wait()方法而被阻塞的线程会被放在该队列中_WaitSetLock = 0 ;_Responsible = NULL;_succ = NULL;_cxq = NULL; // 竞争队列,所有请求锁的线程首先会被放在这个队列中FreeNext = NULL;_EntryList = NULL; // 阻塞;第二轮竞争锁仍然没有抢到的线程(偏向/可重入)_SpinFreq = 0;_SpinClock = 0;OwnerIsThread = 0;}
结论:正好印证了上面的流程图
3.2 HotSpot源码之Monitor竞争
目标: 通过JVM虚拟机源码分析synchronized多个线程抢夺锁,拿到锁之后要干什么?
monitorenter指令执行:JVM源码:src/share/vm/interpreter/interpreterRuntime.cppJVM函数: InterpreterRuntime::monitorenter函数
//锁竞争InterpreterRuntime::monitorenterIRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread,BasicObjectLock* elem))#ifdef ASSERT thread->last_frame().interpreter_frame_verify_monitor(elem);#endif if (PrintBiasedLockingStatistics) { Atomic::inc(BiasedLocking::slow_path_entry_count_addr());} Handle h_obj(thread, elem->obj()); assert(Universe::heap()->is_in_reserved_or_null(h_obj()), "must be NULL or an object"); //偏向锁(非锁:jdk14废弃) if (UseBiasedLocking) { // Retry fast entry if bias is revoked to avoid unnecessary inflation ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);} else { // 重量级锁,最终调用了objectMonitor.cpp中的ObjectMonitor::enter ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK); ...略
最终调用objectMonitor.cpp文件中的 ObjectMonitor::enter
src/share/vm/runtime/objectMonitor.cpp
//重量级锁入口void ATTR ObjectMonitor::enter(TRAPS) { Thread * const Self = THREAD ; void * cur ;// 1、通过CAS(原子操作)操作尝试把monitor的_owner字段设置为当前线程(开始竞争) cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ; if (cur == NULL) { // Either ASSERT _recursions == 0 or explicitly set _recursions = 0. assert (_recursions == 0 , "invariant") ; assert (_owner == Self, "invariant") ; // CONSIDER: set or assert OwnerIsThread == 1 return ;}// 2、拿到锁;计数+1,recursions++ if (cur == Self) { _recursions ++ ;//第一次进入(计数+1) return ;} if (Self->is_lock_owned ((address)cur)) { assert (_recursions == 0, "internal state error"); _recursions = 1 ; _owner = Self ; OwnerIsThread = 1 ; return ;} assert (Self->_Stalled == 0, "invariant") ; Self->_Stalled = intptr_t(this) ; if (Knob_SpinEarly && TrySpin (Self) > 0) { assert (_owner == Self , "invariant") ; assert (_recursions == 0 , "invariant") ; assert (((oop)(object()))->mark() == markOopDesc::encode(this),"invariant") ; Self->_Stalled = 0 ; return ;} assert (_owner != Self , "invariant") ; assert (_succ != Self , "invariant") ; assert (Self->is_Java_thread() , "invariant") ; JavaThread * jt = (JavaThread *) Self ; assert (!SafepointSynchronize::is_at_safepoint(), "invariant") ; assert (jt->thread_state() != _thread_blocked , "invariant") ; assert (this->object() != NULL , "invariant") ; assert (_count >= 0, "invariant") ; Atomic::inc_ptr(&_count); EventJavaMonitorEnter event;{ JavaThreadBlockedOnMonitorEnterState jtbmes(jt, this); DTRACE_MONITOR_PROBE(contended__enter, this, object(), jt); if (JvmtiExport::should_post_monitor_contended_enter()) { JvmtiExport::post_monitor_contended_enter(jt, this); } OSThreadContendState osts(Self->osthread()); ThreadBlockInVM tbivm(jt); Self->set_current_pending_monitor(this); for (;;) { jt->set_suspend_equivalent(); // cleared by handle_special_suspend_equivalent_condition() // or java_suspend_self()// 3、获取锁失败的线程,则等待!!!!!!!!!!!!!!!!!!!!!!!! EnterI (THREAD) ; if (!ExitSuspendEquivalent(jt)) break ; _recursions = 0 ; _succ = NULL ; exit (false, Self) ; jt->java_suspend_self(); } Self->set_current_pending_monitor(NULL);}
总结
通过CAS尝试把monitor的owner字段设置为当前线程。如果设置之前的owner指向当前线程,说明当前线程再次进入monitor,即重入锁,执行recursions ++ ,记录重入的次数。获取锁失败的线程,则【等待】锁的释放。一句话总结:自旋拿锁、拿到+1 、拿不到等待(竞争队列)
3.3 HotSpot源码之Monitor等待
目标: 通过JVM虚拟机源码分析synchronized拿不到锁的线程他们都去干什么了?
还是 /objectMonitor.cpp还是EnterI函数路径:src/share/vm/runtime/objectMonitor.cpp的
//拿不到锁的线程他们都去干什么了??void ATTR ObjectMonitor::EnterI (TRAPS) { Thread * Self = THREAD ; assert (Self->is_Java_thread(), "invariant") ; assert (((JavaThread *) Self)->thread_state() == _thread_blocked ,"invariant") ; // 没拿到锁,还是要尝试TryLock一次 if (TryLock (Self) > 0) { //拿到锁执行,在返回 assert (_succ != Self , "invariant") ; assert (_owner == Self , "invariant") ; assert (_Responsible != Self , "invariant") ; return ;//成功获取 } DeferredInitialize () ; //没拿到锁,开始TrySpin自旋(CAS,while循环) if (TrySpin (Self) > 0) { assert (_owner == Self , "invariant") ; assert (_succ != Self , "invariant") ; assert (_Responsible != Self , "invariant") ; return ; } assert (_succ != Self , "invariant") ; assert (_owner != Self , "invariant") ; assert (_Responsible != Self , "invariant") ;// 实在拿不到锁;当前线程被封装成ObjectWaiter对象node,状态设置成ObjectWaiter::TS_CXQ//即将放入竞争队列 ObjectWaiter node(Self) ; Self->_ParkEvent->reset() ; node._prev = (ObjectWaiter *) 0xBAD ; node.TState = ObjectWaiter::TS_CXQ ; ObjectWaiter * nxt ; for (;;) { node._next = nxt = _cxq ; //使用内核函数cmpxchg_ptr 将没有拿到锁线程(node)放到竞争队列 if (Atomic::cmpxchg_ptr (&node, &_cxq, nxt) == nxt) break ; if (TryLock (Self) > 0) { assert (_succ != Self , "invariant") ; assert (_owner == Self , "invariant") ; assert (_Responsible != Self , "invariant") ; return ; } } if ((SyncFlags & 16) == 0 && nxt == NULL && _EntryList == NULL) { Atomic::cmpxchg_ptr (Self, &_Responsible, NULL) ; } TEVENT (Inflated enter - Contention) ; int nWakeups = 0 ; int RecheckInterval = 1 ;//将竞争队列线程挂起 for (;;) {// 线程在被挂起前做一下挣扎,看能不能获取到锁 if (TryLock (Self) > 0) break ; assert (_owner != Self, "invariant") ; if ((SyncFlags & 2) && _Responsible == NULL) { Atomic::cmpxchg_ptr (Self, &_Responsible, NULL) ; } // park self if (_Responsible == Self (SyncFlags & 1)) { TEVENT (Inflated enter - park TIMED) ; Self->_ParkEvent->park ((jlong) RecheckInterval) ; // Increase the RecheckInterval, but clamp the value. RecheckInterval *= 8 ; if (RecheckInterval > 1000) RecheckInterval = 1000 ; } else { TEVENT (Inflated enter - park UNTIMED) ; // 挂起!!!!!!::通过park将当前线程挂起(不被执行了),等待被唤醒!!!!!!!!!!! Self->_ParkEvent->park() ; } //当该线程被唤醒时,执行TryLock----->ObjectMonitor::TryLoc !!!!!!!!!!!!!!!!!!!!! if (TryLock(Self) > 0) break ;
当该线程被唤醒时,会从挂起的点继续执行,通过 ObjectMonitor::TryLock 尝试获取锁总结4. 竞争失败的线程被封装成ObjectWaiter对象node,状态设置成ObjectWaiter::TS_CXQ(竞争队列)5. 在for循环中,通过CAS把node节点push到_cxq列表中,(竞争队列)6. node节点push到_cxq列表之后,通过自旋尝试获取锁,如果还是没有获取到锁,则通过park将当前线程挂起,等待被唤醒。7. 当该线程被唤醒时,会从挂起的点继续执行,通过 ObjectMonitor::TryLock 尝试获取锁。
一句话总结:没拿到,尝试拿一次、在自旋去拿、实在拿不到就去竞争队列、等待唤醒
3.4 HotSpot源码之Monitor释放
目标: 通过JVM虚拟机源码分析synchronized拿到锁的线程最后是怎么释放锁的?
执行monitorexit指令还是 /objectMonitor.cpp里面的exit函数Osrc/share/vm/runtime/objectMonitor.cpp
//线程释放调用exit方法void ATTR ObjectMonitor::exit(bool not_suspended, TRAPS) { Thread * Self = THREAD ; if (THREAD != _owner) { if (THREAD->is_lock_owned((address) _owner)) { assert (_recursions == 0, "invariant") ; _owner = THREAD ; _recursions = 0 ; OwnerIsThread = 1 ; } else { TEVENT (Exit - Throw IMSX) ; assert(false, "Non-balanced monitor enter/exit!"); if (false) { THROW(vmSymbols::java_lang_IllegalMonitorStateException()); } return; } }//_recursions计数不等于0;说明还没出代码块;进入减减操作, if (_recursions != 0) { _recursions--; // this is simple recursive enter TEVENT (Inflated exit - recursive) ; return ; } if ((SyncFlags & 4) == 0) { _Responsible = NULL ; }#if INCLUDE_TRACE if (not_suspended && Tracing::is_event_enabled(TraceJavaMonitorEnterEvent)) { _previous_owner_tid = SharedRuntime::get_java_tid(Self); }#endif for (;;) { assert (THREAD == _owner, "invariant") ; if (Knob_ExitPolicy == 0) { OrderAccess::release_store_ptr (&_owner, NULL) ; // drop the lock OrderAccess::storeload() ; // See if we need towake a successor if ((intptr_t(_EntryList)intptr_t(_cxq)) == 0 _succ != NULL) { TEVENT (Inflated exit - simple egress) ; return ; } TEVENT (Inflated exit - complex egress) ; if (Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) != NULL) { return ; } TEVENT (Exit - Reacquired) ; } else { if ((intptr_t(_EntryList)intptr_t(_cxq)) == 0 _succ != NULL) { OrderAccess::release_store_ptr (&_owner, NULL) ; // drop the lock OrderAccess::storeload() ; // Ratify the previously observed values. if (_cxq == NULL _succ != NULL) { TEVENT (Inflated exit - simple egress) ; return ; } if (Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) != NULL) { TEVENT (Inflated exit - reacquired succeeded) ; return ; } TEVENT (Inflated exit - reacquired failed) ; } else { TEVENT (Inflated exit - complex egress) ; } } guarantee (_owner == THREAD, "invariant") ;// 计数为0;开始唤醒cq竞争队列、enteryList阻塞队列 ObjectWaiter * w = NULL ;//w就是被唤醒的线程 int QMode = Knob_QMode ;// qmode = 2:直接绕过EntryList阻塞队列,从cxq(竞争)队列中获取线程用于竞争锁 if (QMode == 2 && _cxq != NULL) { w = _cxq ; assert (w != NULL, "invariant") ; assert (w->TState == ObjectWaiter::TS_CXQ, "Invariant") ; ExitEpilog (Self, w) ; return ; }// qmode =3:cxq(竞争)队列插入EntryList(阻塞)尾部; if (QMode == 3 && _cxq != NULL) { w = _cxq ; for (;;) { assert (w != NULL, "Invariant") ; ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL,&_cxq, w) ; if (u == w) break ; w = u ; } assert (w != NULL , "invariant") ; ObjectWaiter * q = NULL ; ObjectWaiter * p ; for (p = w ; p != NULL ; p = p->_next) { guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ; p->TState = ObjectWaiter::TS_ENTER ; p->_prev = q ; q = p ; } // Append the RATs to the EntryList // TODO: organize EntryList as a CDLL so we can locate the tail inconstant-time. ObjectWaiter * Tail ; for (Tail = _EntryList ; Tail != NULL && Tail->_next != NULL ; Tail =Tail->_next) ; if (Tail == NULL) { _EntryList = w ; } else { Tail->_next = w ; w->_prev = Tail ; } }// qmode =4:cxq队列插入到_EntryList头部 if (QMode == 4 && _cxq != NULL) { // Aggressively drain cxq into EntryList at the first opportunity. // This policy ensure that recently-run threads live at the head ofEntryList. // Drain _cxq into EntryList - bulk transfer. // First, detach _cxq. // The following loop is tantamount to: w = swap (&cxq, NULL) w = _cxq ; for (;;) { assert (w != NULL, "Invariant") ; ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL,&_cxq, w) ; if (u == w) break ; w = u ; } assert (w != NULL , "invariant") ; ObjectWaiter * q = NULL ; ObjectWaiter * p ; for (p = w ; p != NULL ; p = p->_next) { guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ; p->TState = ObjectWaiter::TS_ENTER ; p->_prev = q ; q = p ; } // Prepend the RATs to the EntryList if (_EntryList != NULL) { q->_next = _EntryList ; _EntryList->_prev = q ; } _EntryList = w ; // Fall thru into code that tries to wake a successor from EntryList } w = _EntryList ; if (w != NULL) { assert (w->TState == ObjectWaiter::TS_ENTER, "invariant") ; ExitEpilog (Self, w) ;//唤醒w!!!!!!!!!!!!!!!!!!!!!! ------->当前类的ExitEpilog return ; }
实现如下
void ObjectMonitor::ExitEpilog (Thread * Self, ObjectWaiter * Wakee) {assert (_owner == Self, "invariant") ;_succ = Knob_SuccEnabled ? Wakee->_thread : NULL ;ParkEvent * Trigger = Wakee->_event ;Wakee = NULL ;// Drop the lockOrderAccess::release_store_ptr (&_owner, NULL) ;OrderAccess::fence()&
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。