目录

  • 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的所有权。其过程如下:

  1. 若monior的进入数为0,线程可以进入monitor,并将monitor的进入数置为1。当前线程成为
    monitor的owner(所有者)
  2. 若线程已拥有monitor的所有权,允许它重入monitor,则进入monitor的进入数加1
  3. 若其他线程已经占有monitor的所有权,那么当前尝试获取monitor的所有权的线程会被阻塞,直
    到monitor的进入数变为0,才能重新尝试获取monitor的所有权。
    monitorexit(重要)
  4. 能执行monitorexit指令的线程一定是拥有当前对象的monitor的所有权的线程。
  5. 执行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);
}

总结

  1. 通过CAS尝试把monitor的owner字段设置为当前线程。
  2. 如果设置之前的owner指向当前线程,说明当前线程再次进入monitor,即重入锁,执行
    recursions ++ ,记录重入的次数。
  3. 获取锁失败的线程,则【等待】锁的释放。
    一句话总结:自旋拿锁、拿到+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底层源码和原理剖析相关推荐

  1. 死磕 java同步系列之ReentrantReadWriteLock源码解析

    问题 (1)读写锁是什么? (2)读写锁具有哪些特性? (3)ReentrantReadWriteLock是怎么实现读写锁的? (4)如何使用ReentrantReadWriteLock实现高效安全的 ...

  2. Java中的锁大全(底层源码分析)

    引用:https://tech.meituan.com/2018/11/15/java-lock.html 加锁过程:https://www.cnblogs.com/hkdpp/p/11917383. ...

  3. 偏向锁、轻量级锁、重量级锁,Synchronized底层源码终极解析!

    synchronized是掌握Java高并发的关键知识点,底层源码更是面试重灾区.本文从源码学习synchronized的原理,讲解对象头.偏向锁.轻量级锁.重量级锁等概念. 扫码关注<Java ...

  4. java condition_死磕 java同步系列之ReentrantLock源码解析(二)

    (手机横屏看源码更方便) 问题 (1)条件锁是什么? (2)条件锁适用于什么场景? (3)条件锁的await()是在其它线程signal()的时候唤醒的吗? 简介 条件锁,是指在获取锁之后发现当前业务 ...

  5. Java设计模式学习以及底层源码分析

    源码在分支master 工厂模式 把具体创建产品的细节封装起来,你要什么产品,我给你什么产品即可. 简单工厂模式 工厂方法模式 缓存层:抽象类 抽象工厂模式 缓存层是:接口 原型模式 问题: 原型模式 ...

  6. 深读源码-java同步系列之StampedLock源码解析

    问题 (1)StampedLock是什么? (2)StampedLock具有什么特性? (3)StampedLock是否支持可重入? (4)StampedLock与ReentrantReadWrite ...

  7. phaser java_死磕 java同步系列之Phaser源码解析

    问题 (1)Phaser是什么? (2)Phaser具有哪些特性? (3)Phaser相对于CyclicBarrier和CountDownLatch的优势? 简介 Phaser,翻译为阶段,它适用于这 ...

  8. concurrenthashmap实现原理_Mybatis:PageHelper分页插件源码及原理剖析

    PageHelper是一款好用的开源免费的Mybatis第三方物理分页插件,其实我并不想加上好用两个字,但是为了表扬插件作者开源免费的崇高精神,我毫不犹豫的加上了好用一词作为赞美. 原本以为分页插件, ...

  9. PageHelper分页插件源码及原理剖析

    摘要: com.github.pagehelper.PageHelper是一款好用的开源免费的Mybatis第三方物理分页插件. PageHelper是一款好用的开源免费的Mybatis第三方物理分页 ...

最新文章

  1. Python实现JSON反序列化类对象
  2. Segment Routing — SRv6 — SRv6 协议解析
  3. RestTemplate技术预研-认识RestTemplate
  4. router-link-active 和 router-link-exact-active router-link-active
  5. 网站内容排版可用性分析
  6. HDU3790 最短路径问题【Dijkstra算法】
  7. python散点图数据怎么输入_python 散点图添加标签
  8. Js+fromdata
  9. Android中的Drawable(一)
  10. Windows列出系统所有补丁(wmic)
  11. android 脚本swipe,appium1.1 版本使用 swipe 方法报错
  12. 合肥工业大学计算机与信息学院导师介绍,合肥工业大学计算机与信息学院硕士生导师:方静副教授...
  13. DayDayUp:计算机技术与软件专业技术资格证书之《系统集成项目管理工程师》课程讲解之项目管理概述、项目管理基本基础知识、项目立项管理相关知识
  14. 华为云EulerOS安装docker
  15. css中实现三角形的几种方式
  16. 红绿灯代码 摘抄抖音 渡一前端的
  17. Power BI 字符串填充一些特定的字符补齐位数
  18. Jieba库实现词性标注及小说人物角色抽取
  19. 多元线性回归分析示例
  20. 荣耀正式更换了Logo,从此再也没有‘华为荣耀’之说

热门文章

  1. EasyNVR接入EasyNVS显示授权超时是什么原因?如何解决?
  2. WP采集插件免费wordpress采集挂机插件
  3. python爬取猫眼电影数据
  4. Container is running beyond physical memory limits
  5. 媒体揭露互联网“账号黑市”:百倍暴利
  6. 【VulnHub靶场】——BOREDHACKERBLOG: CLOUD AV
  7. 电子电气架构车载网关系列——网关主要应用场景及相关要求
  8. vue watch的用法及新旧值一样问题解决
  9. webpack二刷之五、生产环境优化(3.sideEffects 副作用)
  10. 测试用例设计方法_判定表法(游戏向)