java的io高级面试题,Java的wait和notify学习三部曲之一,高级java面试题及答案

// and must not look locked either.

lock->set_displaced_header(markOopDesc::unused_mark());

//锁膨胀

ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);

}

线程C在上面代码中的执行顺序如下:

  1. 判断是否是无锁状态,如果是就通过Atomic::cmpxchg_ptr去竞争锁;

  2. 不是无锁状态,就检查当前锁是否是线程C持有;

  3. 不是线程C持有,调用inflate方法开始锁膨胀;

ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);

来看看锁膨胀的源码:

640?wx_fmt=png

如上图,锁膨胀的代码太长,我们这里只看关键代码吧:红框中,如果当前状态已经是重量级锁,就通过mark->monitor()方法取得ObjectMonitor指针再返回;绿框中,如果还不是重量级锁,就检查是否处于膨胀中状态(其他线程正在膨胀中),如果是膨胀中,就调用ReadStableMark方法进行等待,ReadStableMark方法执行完毕后再通过continue继续检查,ReadStableMark方法中还会调用os::NakedYield()释放CPU资源;

如果红框和绿框的条件都没有命中,目前已经是轻量级锁了(不是重量级锁并且不处于锁膨胀状态),可以开始膨胀了,如下图:

640?wx_fmt=png

简单来说,锁膨胀就是通过CAS将监视器对象OjectMonitor的状态设置为INFLATING,如果CAS失败,就在此循环,再走前一副图中的的红框和绿框中的判断,如果CAS设置成功,会继续设置ObjectMonitor中的header、owner等字段,然后inflate方法返回监视器对象OjectMonitor;

看看之前slow_enter方法中,调用inflate方法的代码如下:

ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);

所以inflate方法返回监视器对象OjectMonitor之后,会立刻执行OjectMonitor的enter方法,这个方法中开始竞争锁了,方法在openjdk/hotspot/src/share/vm/runtime/objectMonitor.cpp文件中:

640?wx_fmt=png

如上图,红框中表示OjectMonitor的enter方法一进来就通过CAS将OjectMonitor的owner设置为当前线程,绿框中表示设置成功的逻辑,第一个if表示重入锁的逻辑,第二个if表示第一次设置owner成功,都意味着竞争锁成功,而我们的线程C显然是竞争失败的,会进入下图中的无线循环,反复调用EnterI方法:

640?wx_fmt=png

进入EnterI方法看看:

640?wx_fmt=png

如上图,首先构造一个ObjectWaiter对象node,后面的for(;;)代码块中来是一段非常巧妙的代码,同一时刻可能有多个线程都竞争锁失败走进这个EnterI方法,所以在这个for循环中,用CAS将cxq地址放入node的next,也就是把node放到cxq队列的首位,如果CAS失败,就表示其他线程把node放入到cxq的首位了,所以通过for循环再放一次,只要成功,此node就一定在最新的_cxq队列的首位。

接下来的代码又是一个无限循环,如下图:

640?wx_fmt=png

从上图可以看出,进入循环后先调用TryLock方法竞争一次锁,如果成功了就退出循环,否则就调用Self->_ParkEvent->park方法使线程挂起,这里有自旋锁的逻辑,也就是park方法带了时间参数,就会在挂起一段时间后自动唤醒,如果不是自旋的条件,就一直挂起等待被其他条件唤醒,线程被唤醒后又会执行TryLock方法竞争一次锁,竞争不到继续这个for循环;

到这里我们已经把线程C在BLOCK的时候的逻辑理清楚了,小结如下:

  1. 偏向锁逻辑,未命中;

  2. 如果是无锁状态,就通过CAS去竞争锁,此处由于锁已经被线程B持有,所以不是无锁状态;

  3. 不是无锁状态,而且锁不是线程C持有,执行锁膨胀,构造OjectMonitor对象;

  4. 竞争锁,竞争失败就将线程加入_cxq队列的首位;

  5. 开始无限循环,竞争锁成功就退出循环,竞争失败线程挂起,等待被唤醒后继续竞争;

线程B在notify()的时候做了什么

接下来该线程B执行notify了,代码是objectMonitor.cpp的ObjectMonitor::notify方法:

640?wx_fmt=png

如上图所示,首先是Policy的赋值,其次是调用DequeueWaiter()方法将WaitSet队列的第一个值取出并返回,还记得WaitSet么?所有wait的线程都被包装成ObjectWaiter对象然后放进来了;接下来对ObjectWaiter对象的处理方式,根据Policy的不同而不同:Policy == 0:放入EntryList队列的排头位置;Policy == 1:放入EntryList队列的末尾位置;Policy == 2:EntryList队列为空就放入EntryList,否则放入_cxq队列的排头位置;

640?wx_fmt=png

如上图所示,请注意把ObjectWaiter的地址写到cxq变量的时候要用CAS操作,因为此时可能有其他线程正在竞争锁,竞争失败的时候会将自己包装成ObjectWaiter对象加入到cxq中;

这里的代码有一处疑问,期待着读着您的指教:如果_EntryList为空,就把ObjectWaiter放入ObjectWaiter中,为什么要这样做呢?

Policy == 3:放入cxq队列中,末尾位置;更新cxq变量的值的时候,同样要通过CAS注意并发问题;

这里有一段很巧妙的代码,现将cxq保存在Tail中,正常情况下将ObjectWaiter赋值给Tail->next就可以了,但是此时有可能其他线程正在cxq的尾部追加数据了,所以此时Tail对象对应的记录就不是最后一条了,那么它的next就非空了,一旦发生这种情况,就执行Tail = Tail->next,这样就获得了最新的cxq的尾部数据,如下图所示:

640?wx_fmt=png

Policy等于其他值,立即唤醒ObjectWaiter对应的线程;

小结一下,线程B执行notify时候做的事情:

  1. 执行过wait的线程都在队列WaitSet中,此处从WaitSet中取出第一个;

  2. 根据Policy的不同,将这个线程放入EntryList或者cxq队列中的起始或末尾位置;

线程B释放锁的时候做了什么

接下来到了揭开问题的关键了,我们来看objectMonitor.cpp的ObjectMonitor::exit方法;

640?wx_fmt=png

如上图,方法一进来先做一些合法性判断,接下来如红框所示,是偏向锁逻辑,偏向次数减一后直接返回,显然线程B在此处不会返回,而是继续往下执行;

根据QMode的不同,有不同的处理方式:

  1. QMode = 2,并且cxq非空:取cxq队列排头位置的ObjectWaiter对象,调用ExitEpilog方法,该方法会唤醒ObjectWaiter对象的线程,此处会立即返回,后面的代码不会执行了;

  2. QMode = 3,并且cxq非空:把cxq队列首元素放入_EntryList的尾部;

  3. QMode = 4,并且cxq非空:把cxq队列首元素放入_EntryList的头部;

  4. QMode = 0,不做什么,继续往下看;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

最后

面试是跳槽涨薪最直接有效的方式,马上金九银十来了,各位做好面试造飞机,工作拧螺丝的准备了吗?

掌握了这些知识点,面试时在候选人中又可以夺目不少,暴击9999点。机会都是留给有准备的人,只有充足的准备,才可能让自己可以在候选人中脱颖而出。

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

们一起学习成长!**](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值