线程的notify方法_forkjoinpool默认线程数量

线程的notify方法_forkjoinpool默认线程数量从源码角度剖析notify/notifyAll方法到底做了些什么,线程是如何唤醒的。

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

众所周知,使用notify/notifyAll方法能唤醒wait等待的线程,那么在底层源码究竟做了些什么呢?

本章内容要解决的问题

问题1:notify/nofityAll真的唤醒了线程吗?

问题2:notify/nofityAll底层逻辑是怎样的?

线程的notify方法_forkjoinpool默认线程数量

(图1-1)

带着这两个问题来具体探究一下:

在(图1-1)中,java中的notify/nofityAll方法对应c++源码jvm.cpp中的JVM_MonitorNotifyJVM_MonitorNotifyAll方法。

1.首先,进入jvm.cpp文件,查看JVM_MonitorNotify方法。

在 JVM_MonitorNotify方法中,调用了返回值为ObjectSynchronizer的notify方法(图1-2);

线程的notify方法_forkjoinpool默认线程数量

 

(图1-2)

2.再次进入方法,可以看到最终又调用了一个notify方法,继续跟进

线程的notify方法_forkjoinpool默认线程数量

 (图1-3)

 好了,在这里是真正的核心逻辑了  (图1-4):

首先有一个policy策略(默认为2),接着DequeueWaiter,从WaitSet取出ObjectWaiter节点(waitSet等待队列是一个双向循环链表,调用object.wait,会把线程包装为一个ObjectWaiter节点,然后方入这个链表中)

线程的notify方法_forkjoinpool默认线程数量

 线程的notify方法_forkjoinpool默认线程数量

  (图1-4)

 来看看DequeueWaiter的逻辑(从双向链表中取出节点)(图1-5)

​​​​​​​线程的notify方法_forkjoinpool默认线程数量

(图1-5)

重点来了…(图1-6/1-7)

根据policy策略挪动ObjectWaiter节点

根据源码可以看到,

Policy策略:

Policy=0:将ObjectWaiter放入到enteylist队列的排头位置

Policy=1:放入到entrylist队列末尾位置

Policy=2:判断entrylist是否为空,为空就放入到entrylist中,否则放入到cxq队列的排头位置(默认)

Policy=3:判断cxq是否为空,如果为空,直接放入头部,否则放入cxq队列末尾位置

其余情况:直接唤醒线程(unpark) 但这几乎是不可能的,因为jdk默认策略为2 且jvm参数不可修改(除非直接更改源码打包)

线程的notify方法_forkjoinpool默认线程数量

(图1-6)

 线程的notify方法_forkjoinpool默认线程数量

(图1-7)

至此,notify方法结束。

也就是说没有任何有关唤醒的操作。那么第一个问题答案出来了:notify/nofityAll真的唤醒了线程吗?答案是并没有。

那么什么时候唤醒线程呢?稍作回答。

先来看看notifyAll方法源码吧:

1.notifyAll调用了返回值为ObjectSynchronizer的notifyall方法(图1-2);

线程的notify方法_forkjoinpool默认线程数量

 进入看看,也是跟着调用notifyall方法,继续进入

线程的notify方法_forkjoinpool默认线程数量

好了,核心来了… (图1-8)

可以看到notifyall源码,和notify源码几乎是一样的,唯一就是多了个for死循环;

也就是说,notifyall方法其实是循环去执行notify逻辑(从waitset链表中取出节点,然后根据策略挪动节点,直至全部取出),仅此而已

 线程的notify方法_forkjoinpool默认线程数量

 (图1-8)

至此,notifyall方法结束。

=========================================================================

看完了notify/notifyall源码逻辑,其实并没有任何唤醒操作,有的仅仅是挪动节点而已;回到中间提出的问题,那么什么时候唤醒线程呢?

其实是在synchronized代码块退出后,释放锁时根据QMode策略进行唤醒的(图1-9、1-10、1-11)

也就是说在monitorexit方法中的exit方法里(部分代码)

线程的notify方法_forkjoinpool默认线程数量

(图1-9)

 线程的notify方法_forkjoinpool默认线程数量

(图1-10)

 线程的notify方法_forkjoinpool默认线程数量

 (图1-11)

 根据不同的QMode策略挪动线程并唤醒线程

再来看看具体的挪动唤醒策略:

根据QMode策略唤醒:

QMode=2,取cxq头部节点直接唤醒

QMode=3,如果cxq非空,把cxq队列放置到entrylist的尾部(顺序跟cxq一致)

QMode=4,如果cxq非空,把cxq队列放置到entrylist的头部(顺序跟cxq相反)

QMode=0,啥都不做,继续往下走(QMode默认是0)默认是0

Qmode=0的判断逻辑就是先判断entrylist是否为空,如果不为空,则取出第一个唤醒,如

果为空再从cxq里面获取第一个唤醒

最后看看唤醒的方法ExitEpilog:

线程的notify方法_forkjoinpool默认线程数量

总结:

线程的notify/nofityAll方法在jvm源码中并没有唤醒线程,而是从waitSet链表取出一个节点进行挪动(根据policy策略,默认为2,判断entrylist是否为空,为空就放入到entrylist中,否则放入到cxq队列的排头位置),等到真正出了synchronized代码块时,根据QMode策略(默认为0,啥也不做,向下继续执行;entrylist是否为空,不为空取出一个唤醒;为空,从cxq集合取出一个唤醒)​挪动节点然后唤醒

最后,附上整体示意图:

线程的notify方法_forkjoinpool默认线程数量

 notify与policy挪动策略图

线程的notify方法_forkjoinpool默认线程数量

         QMode策略唤醒示意图

最后的问题?

1. waitSet、entryList、cxq是什么?有什么作用?三大队列?

简单解释一下:

多线程的各个方法包括synchronized的实现,与三大队列息息相关。

waitSet是线程等待集合,是一个双向循环链表,调用wait方法的线程将会在里面。

entrylist是线程争抢失败的集合,是一个双向链表。

cxq多线程竞争锁是进入的集合,是一个栈结构。

线程节点在多线程环境下操作时,在三个集合中不断地转换,但同一时间只能在某一个集合中,不能多个集合同时存在。

2.线程的其他方法?

这些问题将在后续文章中解答…感谢各位的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/195970.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • linux基本命令_Git常用命令

    linux基本命令_Git常用命令用一个记一个。修改文件权限:chmod777文件名文件夹及其子文件:chmod-R777文件夹

  • spring espect XX but YY

    spring espect XX but YY注入和查找问题HSFConsumerbean,注入的是beanName=’实际接口名’,type=’HSFSpringConsumerBean’,造成Autowire时查询出来的类型不匹配MybatisMapper的autowire为什么没有类型不匹配的问题,注入时是Mapper的代理类,查询出来却直接是Mapper实现类?@autowiredpr…

    2022年10月21日
  • Web安全:概述_逆向和Web安全

    Web安全:概述_逆向和Web安全文章目录浏览器安全同源策略多进程结构沙箱恶意网址拦截跨站脚本攻击XSS定义示例分类浏览器安全同源策略浏览器的同源策略,限制了来自不同源的“document”或脚本对当前“document”的读取或修改。影响源的因素有:host、子域名、端口、协议等。多进程结构将浏览器的各个功能模块分开,各个浏览器实例分开,这样若一个进程崩溃,也不会影响到其他进程。GoogleChrome是第一个采取多进程架构的浏览器,其主要进程包括:浏览器进程、渲染进程、插件进程、扩展进程等。其中插件进程如flash、ja

  • 图片下划线 html,HTML <U>下划线标签元素 HTML下划线标签「建议收藏」

    图片下划线 html,HTML <U>下划线标签元素 HTML下划线标签「建议收藏」为html字体下划线样式标签,即对文字实现下划线效果。一、认识html下划线标签U1、htmlU下划线标签语法:以开始,以结束。u标签不是单独一个标签,而是有开始有闭合的一对标签,使用时候切记勿忘记结束,完成一组u下划线标签使用。内容HTML结构语法分析图2、下划线u用法被加下划线HTML下划线U使用效果截图二、htmlU下划线标签使用小实例接下来CSS5对html下划线标签进行小小实…

  • 最经典的黑客技术入门知识

    最经典的黑客技术入门知识最经典的黑客技术入门知识 整理:Ackarlix 第一节、什么是黑客以我的理解,“黑客”大体上应该分为“正”、“邪”两类,正派黑客依靠自己掌握的知识帮助系统管理员找出系统中的漏洞并加以完善,而邪派黑客则是通过各种黑客技能对系统进行攻击、入侵或者做其他一些有害于网络的事情,因为邪派黑客所从事的事情违背了《黑客守则》,所以他们真正的名字叫“骇客”(Cracker

  • pycharm2021专业版激活码【最新永久激活】

    (pycharm2021专业版激活码)最近有小伙伴私信我,问我这边有没有免费的intellijIdea的激活码,然后我将全栈君台教程分享给他了。激活成功之后他一直表示感谢,哈哈~https://javaforall.cn/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~1STL…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号