JVM源码分析之Object.wait/notify(All)完全解读

概述

本文其实一直都想写,因为各种原因一直拖着没写,直到开公众号的第一天,有朋友再次问到这个问题,这次让我静心下来准备写下这篇文章,本文有些东西是我自己的理解,比如为什么JDK一开始要这么设计,初衷是什么,没怎么去找相关资料,所以只能谈谈自己的理解,所以大家看到文章之后可以谈谈自己的看法,对于实现部分我倒觉得说清楚问题不大,code is here,看明白了就知道怎么回事了。

Object.wait/notify(All)大家都知道主要是协同线程处理的,大家用得也很多,大概逻辑和下面的用法差不多

image.png

看到上面代码,你会有什么疑惑吗?至少我会有几个问题会问自己: * 为什么进入wait和notify的时候要加synchronized锁 * 既然加了synchronized锁,那当某个线程调用了wait的时候明明还在synchronized块里,其他线程怎么进入到锁里去执行notify的 * 为什么wait方法可能会抛出InterruptedException异常 * 如果有多个线程都进入wait状态,那某个线程调用notify唤醒线程时是否按照顺序唤起那些wait线程 * wait的线程是在某个线程执行完notify之后立马就被唤起吗 * notifyAll又是怎么实现全唤起的 * wait的线程是否会影响load

如果上面这些问题也都是你想了解的,那这篇文章或许能给你一个答案。

为何要加synchronized锁

从实现上来说,这个锁至关重要,正因为这把锁,才能让整个wait/notify玩转起来,当然我觉得其实通过其他的方式也可以实现类似的机制,不过hotspot至少是完全依赖这把锁来实现wait/notify的。

如果要我们来实现这种机制我们会怎么去做,我们知道wait/notify是为了线程间协作而设计的,当我们执行wait的时候让线程挂起,当执行notify的时候唤醒其中一个挂起的线程,那需要有个地方来保存对象和线程之间的映射关系(可以想象一个map,key是对象,value是一个线程列表),当调用这个对象的wait方法时,将当前线程放到这个线程列表里,当调用这个对象的notify方法时从这个线程列表里取出一个来让其继续执行,这样看来是可行的,也比较简单,那现在的问题这种映射关系放到哪里。而synchronized正好也是为线程间协作而设计的,上面碰到的问题它也要解决,或许正因为这样wait和notify的实现就直接依赖synchronzied(monitorenter/monitorexit是jvm规范里要求要去实现的)来实现了,这只是我的理解,可能初衷不是这个原因,这其实也是这篇文章迟迟未写的一个原因吧,因为我无法取证自己的理解是对的,欢迎各位在这块谈谈自己的见解。

wait方法执行后未退出同步块,其他线程如何进入同步块

这个问题其实要回答很简单,因为在wait处理过程中会临时释放同步锁,不过需要注意的是当某个线程调用notify唤起了这个线程的时候,在wait方法退出之前会重新获取这把锁,只有获取了这把锁才会继续执行,想象一下,我们知道wait的方法是被monitorenter和monitorexit包围起来,当我们在执行wait方法过程中如果释放了锁,出来的时候又不拿锁,那在执行到monitorexit指令的时候会发生什么?当然这可以做兼容,不过这实现起来还是很奇怪的。

为什么wait方法可能抛出InterruptedException异常

这个异常大家应该都知道,当我们调用了某个线程的interrupt方法时,对应的线程会抛出这个异常,wait方法也不希望破坏这种规则,因此就算当前线程因为wait一直在阻塞,当某个线程希望它起来继续执行的时候,它还是得从阻塞态恢复过来,因此wait方法被唤醒起来的时候会去检测这个状态,当有线程interrupt了它的时候,它就会抛出这个异常从阻塞状态恢复过来。

这里有两点要注意: * 如果被interrupt的线程只是创建了,并没有start,那等他start之后进入wait态之后也是不能会恢复的 * 如果被interrupt的线程已经start了,在进入wait之前,如果有线程调用了其interrupt方法,那这个wait等于什么都没做,会直接跳出来,不会阻塞

被notify(All)的线程有规律吗

这里要分情况: * 如果是通过notify来唤起的线程,那先进入wait的线程会先被唤起来 * 如果是通过nootifyAll唤起的线程,默认情况是最后进入的会先被唤起来,即LIFO的策略

notify执行之后立马唤醒线程吗

其实这个大家可以验证一下,在notify之后写一些逻辑,看这些逻辑是在其他线程被唤起之前还是之后执行,这个是个细节问题,可能大家并没有关注到这个,其实hotspot里真正的实现是退出同步块的时候才会去真正唤醒对应的线程,不过这个也是个默认策略,也可以改的,在notify之后立马唤醒相关线程。

notifyAll是怎么实现全唤起的

或许大家立马想到这个简单,一个for循环就搞定了,不过在jvm里没实现这么简单,而是借助了monitorexit,上面我提到了当某个线程从wait状态恢复出来的时候,要先获取锁,然后再退出同步块,所以notifyAll的实现是调用notify的线程在退出其同步块的时候唤醒起最后一个进入wait状态的线程,然后这个线程退出同步块的时候继续唤醒其倒数第二个进入wait状态的线程,依次类推,同样这这是一个策略的问题,jvm里提供了挨个直接唤醒线程的参数,不过都很罕见就不提了。

wait的线程是否会影响load

这个或许是大家比较关心的话题,因为关乎系统性能问题,wait/nofity是通过jvm里的park/unpark机制来实现的,在linux下这种机制又是通过pthread_cond_wait/pthread_cond_signal来玩的,因此当线程进入到wait状态的时候其实是会放弃cpu的,也就是说这类线程是不会占用cpu资源。

https://www.imdb.com/list/ls080419900/?publish=publish
https://www.imdb.com/list/ls080419956/?publish=publish
https://www.imdb.com/list/ls080419916/?publish=publish
https://www.imdb.com/list/ls080419990/?publish=publish
https://www.imdb.com/list/ls080419855/?publish=publish
https://www.imdb.com/list/ls080419867/?publish=publish
https://www.imdb.com/list/ls080418035/?publish=publish
https://www.imdb.com/list/ls080418063/?publish=publish
https://www.imdb.com/list/ls080418085/?publish=publish
https://www.imdb.com/list/ls080418713/?publish=publish
https://www.imdb.com/list/ls080418783/?publish=publish
https://www.imdb.com/list/ls080418174/?publish=publish
https://www.imdb.com/list/ls080418113/?publish=publish
https://www.imdb.com/list/ls080418138/?publish=publish
https://www.imdb.com/list/ls080418184/?publish=publish
https://www.imdb.com/list/ls080418326/?publish=publish
https://www.imdb.com/list/ls080418399/?publish=publish
https://www.imdb.com/list/ls080418659/?publish=publish
https://www.imdb.com/list/ls080418638/?publish=publish
https://www.imdb.com/list/ls080418696/?publish=publish
https://www.imdb.com/list/ls080418253/?publish=publish
https://www.imdb.com/list/ls080418274/?publish=publish
https://www.imdb.com/list/ls080418239/?publish=publish
https://www.imdb.com/list/ls080418248/?publish=publish
https://www.imdb.com/list/ls080418280/?publish=publish
https://www.imdb.com/list/ls080418459/?publish=publish
https://www.imdb.com/list/ls080418481/?publish=publish
https://www.imdb.com/list/ls080418875/?publish=publish
https://www.imdb.com/list/ls080418874/?publish=publish
https://www.imdb.com/list/ls080418834/?publish=publish
https://www.imdb.com/list/ls080418869/?publish=publish
https://www.imdb.com/list/ls080418844/?publish=publish
https://www.imdb.com/list/ls080430017/?publish=publish
https://www.imdb.com/list/ls080430045/?publish=publish
https://www.imdb.com/list/ls080430562/?publish=publish
https://www.imdb.com/list/ls080430172/?publish=publish
https://www.imdb.com/list/ls080430111/?publish=publish
https://www.imdb.com/list/ls080430167/?publish=publish
https://www.imdb.com/list/ls080430191/?publish=publish
https://www.imdb.com/list/ls080430314/?publish=publish
https://www.imdb.com/list/ls080430389/?publish=publish
https://www.imdb.com/list/ls080430652/?publish=publish
https://www.imdb.com/list/ls080430614/?publish=publish

展开阅读全文

Python数据分析与挖掘

01-08
92讲视频课+16大项目实战+源码+¥800元课程礼包+讲师社群1V1答疑+社群闭门分享会=99元   为什么学习数据分析?       人工智能、大数据时代有什么技能是可以运用在各种行业的?数据分析就是。       从海量数据中获得别人看不见的信息,创业者可以通过数据分析来优化产品,营销人员可以通过数据分析改进营销策略,产品经理可以通过数据分析洞察用户习惯,金融从业者可以通过数据分析规避投资风险,程序员可以通过数据分析进一步挖掘出数据价值,它和编程一样,本质上也是一个工具,通过数据来对现实事物进行分析和识别的能力。不管你从事什么行业,掌握了数据分析能力,往往在其岗位上更有竞争力。    本课程共包含五大模块: 一、先导篇: 通过分析数据分析师的一天,让学员了解全面了解成为一个数据分析师的所有必修功法,对数据分析师不在迷惑。   二、基础篇: 围绕Python基础语法介绍、数据预处理、数据可视化以及数据分析与挖掘......这些核心技能模块展开,帮助你快速而全面的掌握和了解成为一个数据分析师的所有必修功法。   三、数据采集篇: 通过网络爬虫实战解决数据分析的必经之路:数据从何来的问题,讲解常见的爬虫套路并利用三大实战帮助学员扎实数据采集能力,避免没有数据可分析的尴尬。   四、分析工具篇: 讲解数据分析避不开的科学计算库Numpy、数据分析工具Pandas及常见可视化工具Matplotlib。   五、算法篇: 算法是数据分析的精华,课程精选10大算法,包括分类、聚类、预测3大类型,每个算法都从原理和案例两个角度学习,让你不仅能用起来,了解原理,还能知道为什么这么做。
©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值