前言

线程并发系列文章:

Java 线程基础
Java 线程状态
Java “优雅”地中断线程-实践篇
Java “优雅”地中断线程-原理篇
真正理解Java Volatile的妙用
Java ThreadLocal你之前了解的可能有误
Java Unsafe/CAS/LockSupport 应用与原理
Java 并发"锁"的本质(一步步实现锁)
Java Synchronized实现互斥之应用与源码初探
Java 对象头分析与使用(Synchronized相关)
Java Synchronized 偏向锁/轻量级锁/重量级锁的演变过程
Java Synchronized 重量级锁原理深入剖析上(互斥篇)
Java Synchronized 重量级锁原理深入剖析下(同步篇)
Java并发之 AQS 深入解析(上)
Java并发之 AQS 深入解析(下)
Java Thread.sleep/Thread.join/Thread.yield/Object.wait/Condition.await 详解
Java 并发之 ReentrantLock 深入分析(与Synchronized区别)
Java 并发之 ReentrantReadWriteLock 深入分析
Java Semaphore/CountDownLatch/CyclicBarrier 深入解析(原理篇)
Java Semaphore/CountDownLatch/CyclicBarrier 深入解析(应用篇)
最详细的图文解析Java各种锁(终极篇)
线程池必懂系列

上篇文章已经分析了Java对象头构成、源码及其对象头的调试,本篇将分析偏向锁、轻量级锁、重量级锁的实现及其演变过程。由于涉及到c++源码,估计不少同学没兴趣看,因此重点多以图+源码辅助分析。
通过本篇文章,你将了解到:

1、什么是重量级锁
2、轻量级锁/偏向锁的由来
3、偏向锁的加锁、撤销锁、释放锁
4、轻量级锁的加锁、释放锁
5、偏向锁、轻量级锁、重量级锁的异同点

1、什么是重量级锁

简单例子

    private synchronized void testLock() {//doSomething();}

现有两个线程(t1、t2)同时访问testLock()方法,假若t1先拿到锁并执行同步块里的代码。此时t2也要访问testLock()方法,但是因为锁被t1持有,因此t2只能阻塞等待t1释放锁。
此时,锁的形态称为重量锁。

重量级锁为什么"重"

由上面的例子可以看出,t2因为没有获取到锁然后挂起自己,等待t1释放锁后唤醒自己。线程的挂起/唤醒需要CPU切换上下文,此过程代价比较大,因此称此种锁为重量级锁。
线程挂起/唤醒请移步:Java Unsafe/CAS/LockSupport 应用与原理

2、轻量级锁/偏向锁的由来

轻量级锁的由来

还是上面的例子,假设现在t1、t2是交替执行testLock()方法,此时t1、t2没必要阻塞,因为它们之间没有竞争,也就是不需要重量级锁。
线程之间交替执行临界区的情形下使用的锁称为轻量级锁。

轻量级锁相比重量级锁的优势:

1、每次加锁只需要一次CAS
2、不需要分配ObjectMonitor对象
3、线程无需挂起与唤醒

偏向锁的由来

依旧是上面的例子,假设testLock()始终只有一个线程t1在执行呢?这个时候若是使用轻量级锁,每次t1获取锁都需要进行一次CAS,有点浪费性能。
因此就出现了偏向锁:

当锁偏向某个线程时,该线程再次获取锁时无需CAS,只需要一个简单的比较就可以获取锁,这个过程效率很高。

偏向锁相比轻量级锁的优势:

同一个线程多次获取锁时,无需再次进行CAS,只需要简单比较。

3、偏向锁的加锁、撤销锁、释放锁

上面阐述了这三种锁的由来,这些锁是如何实现的呢?接下来从源码的角度进行分析。这三种锁的基础是对象头,关于对象头的详细分析请查看:Java 对象头分析与使用(Synchronized相关)

**锁的本质是共享变量,因此问题的关键是如何访问这个共享变量。**理解这个对于理解三种锁的演变事半功倍,接下来将重点突出这一信息。
既然涉及到了锁,那么自然而然有加锁/释放锁操作,偏向锁比较特殊还多了个撤销锁的操作。

加锁

先来复习对象头:

可以看到偏向锁里存储了偏向线程的id,epoch,偏向锁标记(biased_lock),锁标记(lock)等信息。这些信息统称为Mark Word。
在看源码之前,先来朴素(脑补)地猜测线程t1获取偏向锁的过程:

1、先判断Mark Word里的线程id是否有值。
1.1、如果没有,说明还没有线程占用锁,则直接将t1的线程id记录到Mark Word里。可能会存在多个线程同时修改Mark Word,因此需要进行CAS修改Mark Word。
1.2、如果已有id值,那么判断分两种情况:
1.2.1、该id是t1的id,则此次获取锁是个重入的过程,直接就获取了。
1.2.2、如果该id不是t1的id,说明已经有其它线程获取了锁,t1想要获取锁就需要走撤销流程。

来看看源码:bytecodeInterpreter.cpp

      CASE(_monitorenter): {//获取对象头,用oop表示对象头 -------->(1)oop lockee = STACK_OBJECT(-1);CHECK_NULL(lockee);BasicObjectLock* limit = istate->monitor_base();BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();BasicObjectLock* entry = NULL;while (most_recent != limit ) {//遍历线程栈,找到对应的空闲的BasicObjectLock (2)if (most_recent->obj() == NULL) entry = most_recent;else if (most_recent->obj() == lockee) break;most_recent++;}if (entry != NULL) {//BasicObjectLock _obj字段指向oop ------>(3)entry->set_obj(lockee);int success = false;uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place;//取出对象头里的Mark WordmarkOop mark = lockee->mark();intptr_t hash = (intptr_t) markOopDesc::no_hash;// 支持偏向锁if (mark->has_bias_pattern()) {uintptr_t thread_ident;uintptr_t anticipated_bias_locking_value;//当前的线程idthread_ident = (uintptr_t)istate->thread();//异或运算结果-------->(4)anticipated_bias_locking_value =(((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &~((uintptr_t) markOopDesc::age_mask_in_place);if  (anticipated_bias_locking_value == 0) {//完全相等,则认为是重入了该锁------>(5)if (PrintBiasedLockingStatistics) {(* BiasedLocking::biased_lock_entry_count_addr())++;}success = true;}else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {//不支持偏向锁了-------->(6)//构造无锁的Mark WordmarkOop header = lockee->klass()->prototype_header();if (hash != markOopDesc::no_hash) {header = header->copy_set_hash(hash);}//CAS 修改Mark Word为无锁状态if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), mark) == mark) {if (PrintBiasedLockingStatistics)(*BiasedLocking::revoked_lock_entry_count_addr())++;}}else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {//epoch 过期了------->(7)//使用当前线程id构造偏向锁markOop new_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 (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), mark) == mark) {if (PrintBiasedLockingStatistics)//成功则获取了锁(* BiasedLocking::rebiased_lock_entry_count_addr())++;}else {//否则进行下一步CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);}success = true;}else {//构造匿名偏向锁---------(8)markOop header = (markOop) ((uintptr_t) mark & ((uintptr_t)markOopDesc::biased_lock_mask_in_place |(uintptr_t)markOopDesc::age_mask_in_place |epoch_mask_in_place));if (hash != markOopDesc::no_hash) {header = header->copy_set_hash(hash);}//构造指向当前线程的偏向锁markOop new_header = (markOop) ((uintptr_t) header | thread_ident);// debugging hintDEBUG_ONLY(entry->lock()->set_displaced_header((markOop) (uintptr_t) 0xdeaddead);)//CAS 修改为偏向当前线程的锁if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), header) == header) {if (PrintBiasedLockingStatistics)(* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;}else {//不成功则进行下一步CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);}success = true;}}if (!success) {//上面尝试使用偏向锁,可惜没有成功,则尝试升级为轻量级锁markOop displaced = lockee->mark()->set_unlocked();entry->lock()->set_displaced_header(displaced);bool call_vm = UseHeavyMonitors;if (call_vm || Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) {// Is it simple recursive case?if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {entry->lock()->set_displaced_header(NULL);} else {CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);}}}UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);} else {istate->set_msg(more_monitors);UPDATE_PC_AND_RETURN(0); // Re-execute}}

代码看起来很多,重点说明标注的(1)~(9)个点:
(1)
oop 表示对象头,里边包括含了Mark Word、Klass Word。

(2)
在basicLock.hpp里,BasicObjectLock 结构如下:

#basicLock.hpp
class BasicObjectLock VALUE_OBJ_CLASS_SPEC {friend class VMStructs;private://BasicLockBasicLock _lock;                                  //对象头oop       _obj;                                   ...
};

继续看BasicLock:

#basicLock.hpp
class BasicLock VALUE_OBJ_CLASS_SPEC {friend class VMStructs;private://存储Mark Wordvolatile markOop _displaced_header;...
};

BasicObjectLock 即是熟知的Lock Record的实现,其包含了两个内容:

1、存储Mark Word的_displaced_header
2、指向对象头的指针:_obj


如图,线程的私有栈里存储着多个Lock Record。

(3)
将Lock Record里的_obj赋值为lockee,也就是_obj表示的是对象头。

(4)
从对象头(lockee)里取出Klass Word,该Word是指向Klass类型的指针,Klass类里有个名为_prototype_header字段, 也是表示Mark Word,里面存储着epoch、偏向锁标记等信息(后面为方便描述,使用Klass替代说明)。此处是取出这些信息并拼接上当前线程id,进而和对象头里的Mark Word进行异或运算,找出不相等的位,接下来就是判断具体Mark Word的哪个部分不相等,从而有不同的处理逻辑。

(5)
如果上面的异或相等,那说明Mark Word里存储有当前线程id,epoch、偏向锁标记都一致,也就是锁被当前线程持有了,此次是个重入的过程。因为已经拥有锁了,所以啥也不干了。

(6)
发现Mark Word里的偏向锁标志位和Klass 里的不同,而Mark Word之前已经判断是偏向锁了,因此可以推断Klass 已经不支持偏向锁了。既然不支持偏向锁了,就修改Mark Word为无锁状态,等待后面升级为轻量级锁/重量级锁。

(7)
发现Mark Word里的epoch与Klass里的不同,则认为发生了批量重偏向,因此可以直接修改Mark Word偏向当前线程。

(8)
如果上述条件都不满足,则认为当前是匿名偏向锁(是偏向锁,但是没有偏向任何线程)。尝试直接修改Mark Word偏向当前线程。

通过上述步骤的分析,结果比较明显了:

1、线程每次尝试获取锁都需要关联Lock Record,并将_obj指向对象头,此时Lock Record与对象头就建立了联系。
2、线程成功将线程id写入Mark Word后即表示该线程获取了该偏向锁

偏向锁状态时Lock Record与对象头关系:

此时_displaced_header字段并没有使用。

用图表示偏向锁获取流程:

好了,再来回顾一下线程t1、t2获取偏向锁的过程:

1、t1获取锁,一开始锁是匿名偏向锁,所以走的是上图步骤4,成功获取锁。
2、t1再次获取锁,因为之前已经获取到锁了,所以走的是上图步骤1,重入获取锁。
3、此时t2尝试获取锁,因为t1正在持有锁,因此走的是上图步骤5。

1、4、5 步骤情景已经涉及到了,剩下2、3步骤下面分析。

撤销锁

偏向锁获取不成功,那么在升级为轻量级锁之前先将锁变为无锁状态,此为偏向锁的撤销过程。
来看看源码入口:

#InterpreterRuntime.cpp
IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))...if (UseBiasedLocking) {//使用偏向锁则进入快速处理流程ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);} else {//升级为轻量级锁ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);}...#synchronizer.cpp
void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) {if (UseBiasedLocking) {if (!SafepointSynchronize::is_at_safepoint()) {//不在安全点执行//可能是撤销,也可能是重偏向BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);//如果是重偏向成功,则退出流程if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) {return;}} else {assert(!attempt_rebias, "can not rebias toward VM thread");//在安全点执行撤销BiasedLocking::revoke_at_safepoint(obj);}assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");}//轻量级锁入口slow_enter (obj, lock, THREAD) ;
}

可以看出撤销分为在安全点撤销和非安全点撤销。
重点说一下非安全点撤销:revoke_and_rebias
里面代码比较多,就不一一贴出了。
用图表示如下:

上面的撤销是在不安全点执行的,因此都会有CAS操作。
上图进行了初步的撤销/重偏向,若是成功则后续会升级为轻量级锁;若是失败,则需要进一步地撤销。

批量重偏向/批量撤销逻辑最终也会调用直接撤销函数,继续来看看直接撤销的流程,实际上就是biasedLocking.cpp#revoke_bias 函数:

可以看出,上图修改Mark Word时并没有使用CAS,因为执行这段代码是在安全点执行的,也就是说只要执行了就能成功。

批量重偏向与批量撤销

经过上面的分析,我们知道:

1、当某个线程持有偏向锁,另一个线程想要获取锁时需要撤销锁。
2、撤销先尝试在不安全点使用CAS修改Mark Word为无锁状态,若还是无法撤销则考虑在安全点撤销,等安全点是比较低效的操作。

因此偏向锁引入了批量重偏向与批量撤销。
当某个类的对象锁撤销次数达到一定阈值,比如达到了20次,那么就会触发批量重偏向的逻辑,修改Klass里的epoch值,并修改当前正在使用该类型锁Mark Word里的epoch值。当线程想要获取偏向锁时,对比当前对象的epoch值与Klass里的epoch值,发现不相等,则认为过期。此时该线程被允许直接CAS修改Mark Word偏向当前线程,就不用再走撤销逻辑了。这部分对应最初分析偏向锁入口的标记(7)。
同样的当撤销次数达到40次时,认为该对象已经不适合应用偏向锁了,因此会修改Klass里的偏向锁标记,更改为不支持偏向锁。当线程想要获取偏向锁时,检查Klass里的偏向锁标记值,若是不允许偏向,说明之前发生了批量撤销,因此该线程被允许直接CAS修改Mark Word为无锁状态,就不用再走撤销逻辑了。这部分对应最初分析偏向锁入口的标记(6)。

默认的阈值:

批量重偏向与批量撤销是对偏向锁性能的优化。

释放锁

正常的想法是:当线程退出临界区,也就是释放了锁。

#bytecodeInterpreter.cppCASE(_monitorexit): {//对象头oop lockee = STACK_OBJECT(-1);CHECK_NULL(lockee);BasicObjectLock* limit = istate->monitor_base();BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();//遍历线程栈while (most_recent != limit ) {//找到对应的Lock Recordif ((most_recent)->obj() == lockee) {BasicLock* lock = most_recent->lock();markOop header = lock->displaced_header();//设置Lock Record 里的_obj字段 为nullmost_recent->set_obj(NULL);//此处是轻量级锁的释放,先省略UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);}most_recent++;}...}

偏向锁对象头和Lock Record关系前面已经分析:每次尝试获取偏向锁时,先找到空闲的Lock Record,并将Lock Record里的_obj指向对象头,表示这俩建立起了联系。释放锁的时候将这联系切断,_obj=null。
你也许已经发现了Mark Word并没有发生改变,依然是偏向了之前的线程,那还是没释放锁的嘛。的确是,线程退出临界区时候,并没有释放偏向锁,这么做的目的是:

当再次需要获取锁的时候,只需要简单按位运算判断是否是重入,即可快速获取锁,而不用每次都CAS,这也是偏向锁在只有一个线程访问锁的情景下高效的核心所在。

小结

前边花了很大篇幅阐述偏向锁,看起来很复杂,实际上就是撤销部分比较复杂。

1、偏向锁的"锁"即是Mark Word,想要获取锁就需要对Mark Word进行修改,可能会有多线程竞争修改,因此需要借助CAS。
2、因为撤销操作可能需要在安全点执行,效率比较低,多次撤销更会影响效率,因此引入了批量重偏向与批量撤销。
3、偏向锁的重入计数依靠线程栈里Lock Record个数。
4、偏向锁撤销失败,最终会升级为轻量级锁。
5、偏向锁退出时并没有修改Mark Word,也就是没有释放锁。

4、轻量级锁的加锁、释放锁

加锁

偏向锁的撤销操作比较复杂,而轻量级锁的加锁、释放锁则简单得多。


#synchronizer.cpp
void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {//取出Mark WordmarkOop mark = obj->mark();//走到此说明已经不是偏向锁了assert(!mark->has_bias_pattern(), "should not see bias pattern here");if (mark->is_neutral()) {//如果是无锁状态//将Mark Word拷贝到Lock Record的_displaced_header 字段里lock->set_displaced_header(mark);//CAS修改Mark Word使之指向Lock Recordif (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {TEVENT (slow_enter: release stacklock) ;return ;}} elseif (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {//mark->has_locker() -->表示已经是轻量级锁//THREAD->is_lock_owned((address)mark->locker()) 并且是当前线程获取了轻量级锁//这两点说明当前线程重入该锁//直接设置header==nulllock->set_displaced_header(NULL);return;}//走到这说明不能使用轻量级锁,则需要升级为重量级锁

此时,我们发现Lock Record与轻量级锁的关系更加紧密。

偏向锁了没用的_displaced_header用上了,用以存储无锁状态的Mark Word,待释放锁时恢复(保留了hash值等)。
而Mark Word里的锁记录指针指向了Lock Record,表示该Lock Record所在的线程获取了轻量级锁。

释放锁

      #bytecodeInterpreter.cppCASE(_monitorexit): {oop lockee = STACK_OBJECT(-1);CHECK_NULL(lockee);BasicObjectLock* limit = istate->monitor_base();BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();while (most_recent != limit ) {if ((most_recent)->obj() == lockee) {BasicLock* lock = most_recent->lock();markOop header = lock->displaced_header();most_recent->set_obj(NULL);//以上部分和偏向锁释放一致的if (!lockee->mark()->has_bias_pattern()) {//不是偏向模式bool call_vm = UseHeavyMonitors;//header 不为空,说明是线程第一次获取轻量级锁时占用的Lock Recordif (header != NULL || call_vm) {//而header存储的是无锁状态的Mark Word//因此需要将Mark Word修改恢复为之前的无锁状态if (call_vm || Atomic::cmpxchg_ptr(header, lockee->mark_addr(), lock) != lock) {//失败的话,再将obj设置上,为了重量级锁使用most_recent->set_obj(lockee);CALL_VM(InterpreterRuntime::monitorexit(THREAD, most_recent), handle_exception);}}}UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);}most_recent++;}...}

与偏向锁不同的是,轻量级锁是真的释放了锁,因为修改Mark Word为无锁状态了。
你可能会有疑惑:只有拿到锁的线程才会有释放锁的操作,为什么此处还需要CAS呢?

考虑一种情况:线程A获取了轻量级锁,此时线程B也想要获取锁,由于锁被A占用,因此B将锁膨胀为重量级锁(修改了Mark Word)。而此时A还在执行临界区代码,它并不知道Mark Word已经被更改了。所以当A退出临界区释放锁的时候,它不能直接修改Mark Word,于是使用CAS尝试更新Mark Word。若是Mark Word已经改变了,也就是说之前Mark Word是指向线程A的Lock Reocrd指针,现在是指向ObjectMonitor了,当然A的CAS会失败,接着进行下一步判断,最终可能膨胀为重量级锁。若是没有改变,A释放轻量级锁就直接成功了。

小结

1、轻量级锁的"锁"即是Mark Word,想要获取锁就需要对Mark Word进行修改,可能会有多线程竞争修改,因此需要借助CAS。
2、如果初始锁为无锁状态,则每次进入都需要一次CAS尝试修改为轻量级锁,否则判断是否重入。
3、如果不满足2的条件,则膨胀为重量级锁。
4、轻量级锁退出时即释放锁,变为无锁状态。
5、可以看出轻量级锁比较敏感,一旦有线程竞争就会膨胀为重量级锁。

5、偏向锁、轻量级锁、重量级锁的异同点

由上面的分析可知,想要在不安全点获取锁,就得依靠CAS操作,因此理解CAS的原理是深入锁的基础。有关CAS原理与使用请移步:Java Unsafe/CAS/LockSupport 应用与原理

三者的共同点:

1、都需要和Lock Record关联;偏向锁和重量级锁只用到了_obj字段,而轻量级锁用到了_displaced_header。
2、释放锁时都需要修改Lock Record 里的_obj字段。

三者不同点:

1、偏向锁和轻量级锁的"锁"即是Mark Word,而重量级锁的"锁"是ObjectMonitor,此时Mark Word保留了指针指向ObjectMonitor。
2、偏向锁和轻量级锁依靠Lock Record个数来记录重入的次数,而重量级锁通过
ObjectMonitor里的_recursions 整形变量记录。
3、偏向锁和轻量级锁的重入只需要做简单的判断即可,而重量级锁需要通过CAS判断是否是重入。

三者适用场景

1、偏向锁适合在只有一个线程访问锁的场景,在此种场景下,线程只需要执行一次CAS获取偏向锁,后续该线程再次访问该锁时仅仅只需要简单的判断即可获取锁。
2、轻量级锁适合在有多个线程交替访问锁,并且不会发生竞争的场景。此种场景下,线程每次获取锁只需要执行一次CAS即可。
3、重量级锁适合在多线程竞争环境下访问锁,执行临界区的时间比较长,未获取锁的线程将会被挂起,等待拥有锁的线程释放锁而后唤醒它。此种场景下,线程每次都需要进行多次CAS操作,操作失败将会被放入队列里等待唤醒。

值得注意的是:

偏向锁、轻量级锁是在Java1.6(Java 6)提出的用以对重量级锁的改进。
从上面分析我们也知道为了实现偏向锁的撤销,引入了复杂的同步代码,包括在安全点执行等操作,且对 HotSpot 的其他组件产生了影响。这种复杂性已经成为理解代码的障碍,也阻碍了对同步系统进行重构。**因此,在Java 15废弃了偏向锁。**https://openjdk.java.net/jeps/374。

至此,偏向锁、轻量级锁的原理已经阐述完毕,由于篇幅所限,重量级锁的原理下篇分析。

虽然尽量避免贴过多的代码,但还是无法避免贴了一些,不关注源码的同学请直接看每段的小结。若是对更多的源码细节感兴趣,可查看下面的链接,本篇也参考了以下链接:
https://github.com/farmerjohngit/myblog/issues/13
https://github.com/HenryChenV/my-notes/issues/3

本文源码基于jdk1.8。

您若喜欢,请点赞、关注,您的鼓励是我前进的动力

持续更新中,和我一起步步为营系统、深入学习Java/Android

Java Synchronized 偏向锁/轻量级锁/重量级锁的演变过程相关推荐

  1. synchronized锁升级之重量级锁

    目录 一.什么是重量级锁? 二.重量级锁的演示 三.重量级锁的原理 四.锁的优缺点对比 一.什么是重量级锁? 当有大量的线程都在竞争同一把锁的时候,这个时候加的锁,就是重量级锁. 这个重量级锁其实指的 ...

  2. Java synchronized偏向锁、轻量级锁、重量级锁

    简介 synchronized锁共有偏向锁.轻量级锁.重量级锁三种类型,而这三种类型的加锁方式都是相同的,写代码时不用考虑加哪种锁.使用锁时对象首先会变为偏向锁状态,当有其它线程获取锁时会升级为轻量级 ...

  3. Java synchronized偏向锁后hashcode存在哪里?

    今天的文章从下面这张图片开始,这张图片Java开发们应该很熟悉了 我们都知道无锁状态是对象头是有位置存储hashcode的,而变为偏向锁状态是没有位置存储hashcode的,今天我们来通过实现验证这个 ...

  4. java多线程之锁 -- 偏向锁、轻量级锁、自旋锁、重量级锁

    转载至:https://blog.csdn.net/zqz_zqz/article/details/70233767 之前做过一个测试,详情见这篇文章<多线程 +1操作的几种实现方式,及效率对比 ...

  5. 【Java 并发编程】线程锁机制 ( 锁的四种状态 | 无锁状态 | 偏向锁 | 轻量级锁 | 重量级锁 | 锁竞争 | 锁升级 )

    文章目录 一.悲观锁示例 ( ReentrantLock ) 二.重量级锁弊端 三.锁的四种状态 ( 无锁状态 | 偏向锁 | 轻量级锁 | 重量级锁 ) 四.锁的四种状态之间的转换 ( 无锁状态 - ...

  6. Java如何避免重量级锁,Java 中锁是如何一步步膨胀的(偏向锁、轻量级锁、重量级锁)...

    文章目录 重量级锁(Mutex Lock) 偏向锁(比较 ThreadID) 偏向锁获取过程 偏向锁的释放 轻量级锁(自旋) 轻量级锁的加锁过程 轻量级锁的释放 总结 重量级锁(Mutex Lock) ...

  7. 轻量级锁_Java高级架构师-Java锁的升级策略 偏向锁 轻量级锁 重量级锁

    欢迎关注头条号:Java小野猫 这三种锁是指锁的状态,并且是专门针对Synchronized关键字.JDK 1.6 为了减少"重量级锁"的性能消耗,引入了"偏向锁&quo ...

  8. java轻量级和重量级_Java 偏向锁、轻量级锁和重量级锁

    前言 最开始听到偏向锁.轻量级锁和重量级锁的概念的时候,我还以为是 Java 中提供了相应的类库来实现的,结果了解后才发现, 这三个原来是虚拟机底层对 synchronized 代码块的不同加锁方式. ...

  9. Java锁---偏向锁、轻量级锁、自旋锁、重量级锁

    Java锁-偏向锁.轻量级锁.自旋锁.重量级锁 之前做过一个测试,反复执行过多次,发现结果是一样的: 单线程下synchronized效率最高(当时感觉它的效率应该是最差才对): AtomicInte ...

最新文章

  1. 网络推广策略之如何稳定新站的关键词排名?
  2. Redis:MySQL算老几?
  3. Java-二分查找算法
  4. 光伏业务爆发 同景新能源与信义光能签署103MW订单
  5. lisp中怎样调取图形_越玩越聪明的图形思维游戏
  6. 装完系统还要装什么_家里装了空调还要装空气净化系统吗?会不会太浪费了?...
  7. 如何避免mysql回表查询_mysql如何避免回表查询
  8. RT-Thread 学习笔记(四)——添加RTGUI组件
  9. python3内置函数_python3--内置函数
  10. yii2在linux下面无法启用gii
  11. Android 得到函数耗时多少的方法
  12. python vba 区别_VBA和Python该学哪个?
  13. 全国计算机office二级考试内容,2017年国家计算机二级office考试内容
  14. 从Unity导出Obj格式的地形(Terrian)
  15. tp6 gatewayWorker
  16. 机器学习算法(十) 根据幸福感问卷调查做预测
  17. post 防篡改_Cookie防篡改机制
  18. 《两化融合 数字化转型 价值效益参考模型》国家标准全文
  19. 二级管的正向恢复与反向恢复时间
  20. Eclipse安装内存分析工具(Memory Analyzer)

热门文章

  1. uC/OS-II实时系统
  2. SQL之存储过程(procedure)
  3. rabbitmq 心跳机制
  4. 简单游戏引擎开发笔记(一)
  5. ubuntu上踩的坑—阿里云上创建服务器(深度学习)
  6. CV | Emotionally Enhanced Talking Face Generation论文详解及代码实现
  7. 搞机:使用docker-compose10分钟搞定redis,mongodb,mysql三大数据库
  8. Python虚拟环境搭建
  9. samba 服务搭建
  10. Vue项目后台部分4,分类管理以及详情管理,添加和修改,loading效果,窗体弹出