Java同步锁Synchronized底层源码和原理剖析(推荐)

网友投稿 291 2022-07-28


目录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.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::monitorenter

IRT_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_blSPoXGocked , "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 to

wake 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 in

constant-time.

ObjectWaiter * TailSPoXG ;

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") ;

SPoXG 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 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") ;

// 尝试拿到锁返回1

return 1 ;

}

//拿不到锁返回-1

if (true) return -1 ;

}

}

总结1、先进入减减操作,直到为02、为0后,唤醒竞争队列的线程3、唤醒线程后,继续争夺锁,循环前面的步骤(锁竞争-----等待----释放)一句话总结:释放后,进入减减操作、直到为0然后唤醒队列,让他们去争夺锁,循环前面步骤


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java面试synchronized偏向锁后hashcode存址(java中的synchronized)
下一篇:关于jd(关于jdm的网名)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~