Java同步锁Synchronized底层源码和原理剖析
目录
- 1 synchronized场景回顾
- 2 反汇编寻找锁实现原理
- 3 synchronized虚拟机源码
- 3.1 HotSpot源码Monitor生成
- 3.2 HotSpot源码之Monitor竞争
- 3.3 HotSpot源码之Monitor等待
- 3.4 HotSpot源码之Monitor释放
1 synchronized场景回顾
目标:
synchronized回顾(锁分类–>多线程)
概念
synchronized:是Java中的关键字,是一种同步锁。
Java中锁分为以下几种:
乐观锁、悲观锁(syn)
独享锁(syn)、共享锁
公平锁、非公平锁(syn)
互斥锁(syn)、读写锁
可重入锁(syn)
分段锁
synchronized JDK1.6锁升级(无锁 -> 偏向锁 (非锁)-> 轻量级锁 -> 重量级锁(1.6前都是)【面试常问】
tips:
为什么用到锁?大家肯定会想到多线程(并发)
接下来,我们一起简单回顾下多线程特性
多线程特性回顾(面试常问)
原子性
:指一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执
行
可见性
:是指多个线程访问一个资源时,该资源的状态、值信息等对于其他线程都是可见的。
有序性
:指程序中代码的执行顺序 (编译器会重排)
原子性实现回顾
保证了原子性?
com.syn.com.syn.th.SyncAtomicity
package com.syn.com.syn.th;
import java.util.concurrent.TimeUnit;
/*目标:测试原子性问题1、调用正常(不加锁)方法;两个线程都可以正常执行2、调用加锁方法,只能有一个线程正常执行,其他线程排队等候
*/
public class SyncAtomicity {public static void main(String[] args) throws InterruptedException {SyncAtomicity syncAtomicity = new SyncAtomicity();//synchronized修饰实例方法//new Thread(()->syncAtomicity.testSYNC()).start();//new Thread(()->syncAtomicity.testSYNC()).start();//synchronized修饰静态方法new Thread(() -> SyncAtomicity.testSYNCForStatic()).start();new Thread(() -> SyncAtomicity.testSYNCForStatic()).start();//正常方法//new Thread(() -> syncAtomicity.test()).start();//new Thread(() -> syncAtomicity.test()).start();}//加锁方法public synchronized void testSYNC() {System.out.println("进入testSYNC方法>>>>>>>>>>>>>>>>>>>>>");try {//模拟方法体尚未执行完毕TimeUnit.HOURS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}//加锁方法public synchronized static void testSYNCForStatic() {System.out.println("进入testSYNC方法>>>>>>>>>>>>>>>>>>>>>");try {//模拟方法体尚未执行完毕TimeUnit.HOURS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}//正常方法public void test() {System.out.println("进入test方法>>>>>>>>>>>>>>>>>>>>>");try {//模拟方法体尚未执行完毕TimeUnit.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.BTest
javap -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.hpp
hpp可以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.cpp
JVM函数: InterpreterRuntime::monitorenter函数
//锁竞争InterpreterRuntime::monitorenter
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread,
BasicObjectLock* elem))
#ifdef ASSERTthread->last_frame().interpreter_frame_verify_monitor(elem);
#endifif (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 inflationObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
} else {// 重量级锁,最终调用了objectMonitor.cpp中的ObjectMonitor::enterObjectSynchronizer::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 == 1return ;
}
// 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 selfif (_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 enterTEVENT (Inflated exit - recursive) ;return ;}if ((SyncFlags & 4) == 0) {_Responsible = NULL ;}
#if INCLUDE_TRACEif (not_suspended && Tracing::is_event_enabled(TraceJavaMonitorEnterEvent)) {_previous_owner_tid = SharedRuntime::get_java_tid(Self);}
#endiffor (;;) {assert (THREAD == _owner, "invariant") ;if (Knob_ExitPolicy == 0) {OrderAccess::release_store_ptr (&_owner, NULL) ; // drop the lockOrderAccess::storeload() ; // See if we need to
wake a successorif ((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 lockOrderAccess::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 in
constant-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 of
EntryList.// 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 EntryListif (_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!!!!!!!!!!!!!!!!!!!!!! ------->当前
类的ExitEpilogreturn ;}
实现如下
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 lock
OrderAccess::release_store_ptr (&_owner, NULL) ;
OrderAccess::fence() ; // ST _owner vs LD in
unpark()
if (SafepointSynchronize::do_call_back()) {TEVENT (unpark before SAFEPOINT) ;
}
DTRACE_MONITOR_PROBE(contended__exit, this, object(), Self);// 唤醒之前被park()挂起的线程.Trigger->unpark() ;// invoke ObjectMonitor::EnterI 方法,继续竞争if (ObjectMonitor::_sync_Parks != NULL) {ObjectMonitor::_sync_Parks->inc() ;}
}
被唤醒的线程,回到 ObjectMonitor::EnterI (TRAPS) 的第600行,继续执行monitor 的竞争。
// 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) ;
Self->_ParkEvent->park() ;
}
//唤醒之后就开始抢夺锁
if (TryLock(Self) > 0) break ;
TryLock方 法实现如下:
//线程尝试获取锁(or 线程被唤醒后获取)
int ObjectMonitor::TryLock (Thread * Self) {for (;;) {void * own = _owner ;if (own != NULL) return 0 ;//获取if (Atomic::cmpxchg_ptr (Self, &_owner, NULL) == NULL) {// Either guarantee _recursions == 0 or set _recursions = 0.assert (_recursions == 0, "invariant") ;assert (_owner == Self, "invariant") ;// 尝试拿到锁返回1return 1 ;}
//拿不到锁返回-1if (true) return -1 ;}
}
总结
1、先进入减减操作,直到为0
2、为0后,唤醒竞争队列的线程
3、唤醒线程后,继续争夺锁,循环前面的步骤(锁竞争-----等待----释放)
一句话总结:释放后,进入减减操作、直到为0然后唤醒队列,让他们去争夺锁,循环前面步骤
Java同步锁Synchronized底层源码和原理剖析相关推荐
- 死磕 java同步系列之ReentrantReadWriteLock源码解析
问题 (1)读写锁是什么? (2)读写锁具有哪些特性? (3)ReentrantReadWriteLock是怎么实现读写锁的? (4)如何使用ReentrantReadWriteLock实现高效安全的 ...
- Java中的锁大全(底层源码分析)
引用:https://tech.meituan.com/2018/11/15/java-lock.html 加锁过程:https://www.cnblogs.com/hkdpp/p/11917383. ...
- 偏向锁、轻量级锁、重量级锁,Synchronized底层源码终极解析!
synchronized是掌握Java高并发的关键知识点,底层源码更是面试重灾区.本文从源码学习synchronized的原理,讲解对象头.偏向锁.轻量级锁.重量级锁等概念. 扫码关注<Java ...
- java condition_死磕 java同步系列之ReentrantLock源码解析(二)
(手机横屏看源码更方便) 问题 (1)条件锁是什么? (2)条件锁适用于什么场景? (3)条件锁的await()是在其它线程signal()的时候唤醒的吗? 简介 条件锁,是指在获取锁之后发现当前业务 ...
- Java设计模式学习以及底层源码分析
源码在分支master 工厂模式 把具体创建产品的细节封装起来,你要什么产品,我给你什么产品即可. 简单工厂模式 工厂方法模式 缓存层:抽象类 抽象工厂模式 缓存层是:接口 原型模式 问题: 原型模式 ...
- 深读源码-java同步系列之StampedLock源码解析
问题 (1)StampedLock是什么? (2)StampedLock具有什么特性? (3)StampedLock是否支持可重入? (4)StampedLock与ReentrantReadWrite ...
- phaser java_死磕 java同步系列之Phaser源码解析
问题 (1)Phaser是什么? (2)Phaser具有哪些特性? (3)Phaser相对于CyclicBarrier和CountDownLatch的优势? 简介 Phaser,翻译为阶段,它适用于这 ...
- concurrenthashmap实现原理_Mybatis:PageHelper分页插件源码及原理剖析
PageHelper是一款好用的开源免费的Mybatis第三方物理分页插件,其实我并不想加上好用两个字,但是为了表扬插件作者开源免费的崇高精神,我毫不犹豫的加上了好用一词作为赞美. 原本以为分页插件, ...
- PageHelper分页插件源码及原理剖析
摘要: com.github.pagehelper.PageHelper是一款好用的开源免费的Mybatis第三方物理分页插件. PageHelper是一款好用的开源免费的Mybatis第三方物理分页 ...
最新文章
- Python实现JSON反序列化类对象
- Segment Routing — SRv6 — SRv6 协议解析
- RestTemplate技术预研-认识RestTemplate
- router-link-active 和 router-link-exact-active router-link-active
- 网站内容排版可用性分析
- HDU3790 最短路径问题【Dijkstra算法】
- python散点图数据怎么输入_python 散点图添加标签
- Js+fromdata
- Android中的Drawable(一)
- Windows列出系统所有补丁(wmic)
- android 脚本swipe,appium1.1 版本使用 swipe 方法报错
- 合肥工业大学计算机与信息学院导师介绍,合肥工业大学计算机与信息学院硕士生导师:方静副教授...
- DayDayUp:计算机技术与软件专业技术资格证书之《系统集成项目管理工程师》课程讲解之项目管理概述、项目管理基本基础知识、项目立项管理相关知识
- 华为云EulerOS安装docker
- css中实现三角形的几种方式
- 红绿灯代码 摘抄抖音 渡一前端的
- Power BI 字符串填充一些特定的字符补齐位数
- Jieba库实现词性标注及小说人物角色抽取
- 多元线性回归分析示例
- 荣耀正式更换了Logo,从此再也没有‘华为荣耀’之说
热门文章
- EasyNVR接入EasyNVS显示授权超时是什么原因?如何解决?
- WP采集插件免费wordpress采集挂机插件
- python爬取猫眼电影数据
- Container is running beyond physical memory limits
- 媒体揭露互联网“账号黑市”:百倍暴利
- 【VulnHub靶场】——BOREDHACKERBLOG: CLOUD AV
- 电子电气架构车载网关系列——网关主要应用场景及相关要求
- vue watch的用法及新旧值一样问题解决
- webpack二刷之五、生产环境优化(3.sideEffects 副作用)
- 测试用例设计方法_判定表法(游戏向)