JVM源码剖析之Thread类中sleep方法

版本信息:
jdk版本:jdk8u40

写在前面:

大部分的Java程序员知道让线程睡眠的方法是Thread.sleep方法,而这个方法是一个native方法,让很多想知道底层如何让线程睡眠的程序员望而却步。所以笔者特意写在这篇文章,带各位读者剖析一下Thread.sleep方法背后的神秘。

源码剖析:

话不多说,先从Java层面看一下sleep这个方法。

public static native void sleep(long millis) throws InterruptedException;public static void sleep(long millis, int nanos)
throws InterruptedException {// 非法逻辑if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");}// 非法逻辑if (nanos < 0 || nanos > 999999) {throw new IllegalArgumentException("nanosecond timeout value out of range");}// 如果大于500000就算一毫秒,如果没有设置毫秒,那么纳秒单位就四舍五入算一毫秒。if (nanos >= 500000 || (nanos != 0 && millis == 0)) {millis++;}// 调用重载的sleep方法。sleep(millis);
}

这是一个重载的方法,可以单独传入毫秒,也可以传入毫秒和纳秒。不管调用哪一个sleep最终都是调用native的sleep方法,所以接下来需要看底层如何对其实现。

src/share/native/java/lang/Thread.c 文件中有定义sleep的native实现方法。

static JNINativeMethod methods[] = {{"start0",           "()V",        (void *)&JVM_StartThread},{"stop0",            "(" OBJ ")V", (void *)&JVM_StopThread},{"isAlive",          "()Z",        (void *)&JVM_IsThreadAlive},{"suspend0",         "()V",        (void *)&JVM_SuspendThread},{"resume0",          "()V",        (void *)&JVM_ResumeThread},{"setPriority0",     "(I)V",       (void *)&JVM_SetThreadPriority},{"yield",            "()V",        (void *)&JVM_Yield},{"sleep",            "(J)V",       (void *)&JVM_Sleep},{"currentThread",    "()" THD,     (void *)&JVM_CurrentThread},{"countStackFrames", "()I",        (void *)&JVM_CountStackFrames},{"interrupt0",       "()V",        (void *)&JVM_Interrupt},{"isInterrupted",    "(Z)Z",       (void *)&JVM_IsInterrupted},{"holdsLock",        "(" OBJ ")Z", (void *)&JVM_HoldsLock},{"getThreads",        "()[" THD,   (void *)&JVM_GetAllThreads},{"dumpThreads",      "([" THD ")[[" STE, (void *)&JVM_DumpThreads},{"setNativeName",    "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};

这里是一个Thread类中所有native方法的映射表,我们看到sleep映射为JVM_Sleep方法。

所以看到 src/share/vm/prims/jvm.cpp 文件中 JVM_Sleep方法

JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))JVMWrapper("JVM_Sleep");// 改变状态为sleeping中。JavaThreadSleepState jtss(thread);EventThreadSleep event;if (millis == 0) {		// 如果传入的毫秒为0,那么底层为转换为yield方法,而yield仅仅是让出CPU的使用权,让当前线程重新等待被调度if (ConvertSleepToYield) {os::yield();} else {// 如果不支持转换为yield方法,那么会给出一个默认的睡眠时间。ThreadState old_state = thread->osthread()->get_state();thread->osthread()->set_state(SLEEPING);os::sleep(thread, MinSleepInterval, false);thread->osthread()->set_state(old_state);}} else {// 拿到线程在sleep之前的状态。ThreadState old_state = thread->osthread()->get_state();// 把线程状态改变成SLEEPINGthread->osthread()->set_state(SLEEPING);// 因为对于线程的操作只能交给操作系统if (os::sleep(thread, millis, true) == OS_INTRPT) {// 如果睡眠期间被中断,那么抛出中断异常。THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");}// 改回之前的状态。thread->osthread()->set_state(old_state);}
JVM_END

对这里做一个简单的总结:

  1. 改变状态为Sleeping
  2. 如果开发者传入的毫秒为0,这里会根据策略转换成yield,如果不支持转换就会给出默认的睡眠时间
  3. 因为对于线程的操作只能交给操作系统完成,所以这里调用os::sleep方法,接下来会重点分析此方法。
  4. 如果睡眠过程中被中断了,那么会抛出中断异常
  5. 睡眠正常完成后,会把状态改变成之前的状态。

因为我们只关心Linux操作系统,所以看到src/os/linux/vm/os_linux.cpp 文件中sleep方法。

int os::sleep(Thread* thread, jlong millis, bool interruptible) {ParkEvent * const slp = thread->_SleepEvent ;slp->reset() ;OrderAccess::fence() ;// 判断是否响应中断。if (interruptible) {// 拿到进入之前的时间(纳米为单位)jlong prevtime = javaTimeNanos();for (;;) {// 如果被中断了。if (os::is_interrupted(thread, true)) {return OS_INTRPT;}// 拿到最新的时间(纳米为单位)jlong newtime = javaTimeNanos();if (newtime - prevtime < 0) {// 最新的时间小于之前的时间,这不是扯淡么。assert(!Linux::supports_monotonic_clock(), "time moving backwards");} else {// 一秒 = 1000毫秒// 一秒 = 1000000000纳秒// NANOSECS_PER_MILLISEC = 1000000// 这里是获取到当前睡眠的时间,并且从纳秒转换成毫秒。millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC;}// 时间到了,直接退出。if(millis <= 0) {return OS_OK;}prevtime = newtime;{JavaThread *jt = (JavaThread *) thread;ThreadBlockInVM tbivm(jt);// 改变线程状态。OSThreadWaitState osts(jt->osthread(), false /* not Object.wait() */);jt->set_suspend_equivalent();// 睡眠slp->park(millis);}}} else {OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);jlong prevtime = javaTimeNanos();for (;;) {jlong newtime = javaTimeNanos();if (newtime - prevtime < 0) {assert(!Linux::supports_monotonic_clock(), "time moving backwards");} else {millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC;}if(millis <= 0) break ;prevtime = newtime;slp->park(millis);}return OS_OK ;}
}

对这里做一个简单的总结:

  1. 拿到当前线程对应的parkEvent,这个可以理解为提供了底层睡眠和阻塞的API。
  2. 判断是否可以响应中断
  3. 如果响应中断,那么每次循环都会判断是否被中断了
  4. 获取当前时间,此时间是纳秒
  5. 纳秒转换成毫秒,因为底层睡眠时间需要时毫秒单位(这里为什么获取当前时间不直接拿毫秒,因为考虑到精准度的问题)
  6. 调用parkEvent的park方法,进入操作系统睡眠。

考虑到文章的篇幅问题,parkEvent的park方法就不细追了。大家可以黑盒的理解,它就是让当前线程去阻塞,而传入的单位就是阻塞的时间。

总结:

sleep的底层实现并不复杂,但是不看源码是不会知道,如果传入的时间为0会优化成yield方法,并且在底层并不会像Object类中wait方法一样,释放锁资源等等~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/100634.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

openGauss学习笔记-96 openGauss 数据库管理-访问外部数据库-file_fdw

文章目录 openGauss学习笔记-96 openGauss 数据库管理-访问外部数据库-file_fdw96.1 使用file_fdw96.2 注意事项 openGauss学习笔记-96 openGauss 数据库管理-访问外部数据库-file_fdw openGauss的fdw实现的功能是各个openGauss数据库及远程服务器&#xff08;包括数据库、文件…

沪深300期权一个点多少钱?

经中国证监会批准&#xff0c;深圳证券交易所于2019年12月23日上市嘉实沪深300ETF期权合约品种。该产品是以沪深300为标的物的嘉实沪深300ETF交易型指数基金为标的衍生的标准化合约&#xff0c;下文介绍沪深300期权一个点多少钱?本文来自&#xff1a;期权酱 一、沪深300期权涨…

PDF编辑和OCR文字识别工具ABBYY FineReader PDF

ABBYY FineReader PDF是一款专业的OCR文字识别和PDF编辑工具&#xff0c;可以帮助用户更好地处理和管理PDF文档。以下是ABBYY FineReader PDF的一些特点&#xff1a; 1. 文字识别精准&#xff1a;ABBYY FineReader PDF具有强大的OCR文字识别功能&#xff0c;可以将PDF中的文字…

C#导出本机Win32native dll

C# 使用 "3f/DllExport" 工具导出C风格的本机函数 [文 / 张赐荣] 首先&#xff0c;让我们来了解一下什么是争渡读屏软件&#xff0c;以及什么是争渡文本预处理API。争渡读屏软件是一款屏幕朗读软件&#xff0c;用于协助视力障碍人士操作电脑。 争渡文本预处理API是一…

java的amazonaws接口出现无法执行http请求:管道中断

java使用amazonaws的接口上传文件到minio出现以下异常&#xff1a; com.amazonaws.SdkClientException: Unable to execute HTTP request: Broken pipe (Write failed) at com.amazonaws.http.AmazonHttpClient R e q u e s t E x e c u t o r . h a n d l e R e t r y a b l e…

【广州华锐互动】灭火器使用VR教学系统应用于高校消防演练有什么好处?

在科技发展的大潮中&#xff0c;虚拟现实&#xff08;VR&#xff09;技术以其独特的沉浸式体验赢得了各个领域的青睐&#xff0c;其中包括教育和培训。在高校消防演练中&#xff0c;VR也成为了一种新的消防教育方式。 由广州华锐互动开发的VR消防演练系统&#xff0c;就包含了校…

神经网络(MLP多层感知器)

分类 神经网络可以分为多种不同的类型&#xff0c;下面列举一些常见的神经网络类型&#xff1a; 前馈神经网络&#xff08;Feedforward Neural Network&#xff09;&#xff1a;前馈神经网络是最基本的神经网络类型&#xff0c;也是深度学习中最常见的神经网络类型。它由若干个…

【工具软件】mediamtx——网页、vue3项目中播放 rtsp 视频流(支持265转码)

声明 本文只做 mediamtx 的使用实操&#xff0c;请务必参考下面的博客,&#xff0c;我也参考下面的大佬博客&#xff0c;感谢唯一602的无私分享&#xff1a; 在web页面中直接播放rtsp视频流&#xff0c;重点推荐&#xff1a;mediamtx&#xff0c;不仅仅是rtsp mediamtx 介绍 …

C++ 与基本数据类型:整型、布尔型与字符型

文章目录 参考描述数据类型基本数据类型与复合数据类型静态数据类型 整形数据类型有符号整型数据类型无符号整型数据类型符号位 最少内存空间概念确定大小sizeof 运算符 进制C 中的不同进制数值表示cout 与进制转化影响范围二进制 后缀字面量整型字面量的默认数据类型主动权整型…

代码随想录算法训练营第六十天 | 单调栈 part 1 | 739. 每日温度、496.下一个更大元素 I

目录 739. 每日温度思路代码 496.下一个更大元素 I思路代码 739. 每日温度 Leetcode 思路 维持一个单调递增的栈&#xff0c;向栈逐一pushtemperatures里的index。 如果当前遍历的元素大于栈顶元素&#xff0c;这意味着 栈顶元素的 右边的最大的元素就是 当前遍历的元素&…

【网络安全】网络安全的最后一道防线——“密码”

网络安全的最后一道防线——“密码” 前言超星学习通泄露1.7亿条信息事件武汉市地震监测中心遭境外网络攻击事件 一、密码起源1、 古代密码2、近代密码3、现代密码4、量子密码 二、商密专栏推荐三、如何利用密码保护账号安全&#xff1f;1、账号安全的三大危险&#xff1f;&…

修炼k8s+flink+hdfs+dlink(四:k8s(一)概念)

一&#xff1a;概念 1. 概述 1.1 kubernetes对象. k8s对象包含俩个嵌套对象字段。 spec&#xff08;规约&#xff09;&#xff1a;期望状态 status&#xff08;状态&#xff09;&#xff1a;当前状态 当创建对象的时候&#xff0c;会按照spec的状态进行创建&#xff0c;如果…

Kotlin的作用域函数 let、also、with、run、apply

作用域函数主要有下面这几种&#xff0c;apply &#xff0c;with 、run 、let 、以及 also 。这些函数非常类似&#xff0c;它们的主要区别&#xff1a; 引⽤上下⽂对象的⽅式 &#xff08;this / it&#xff09;返回值 他们在开发中的使用场景主要有两个&#xff0c;一是非空…

javascript中map和filter的区别与联系

javascript中map和filter的区别与联系如何获取对象数组中某个值 javascript中map和filter的区别与联系 在 JavaScript 中&#xff0c;map 和 filter 是两个常用的数组方法&#xff0c;用于对数组进行转换和过滤操作。它们的区别和联系如下&#xff1a; 功能不同&#xff1a; m…

【C++11算法】is_sorted、is_sorted_until

文章目录 前言一、is_sorted函数1.1 is_sorted是什么1.2 函数原型1.3 示例代码1二、is_sorted_until2.1 is_sorted_until是什么&#xff1f;2.2 函数原型2.3示例代码2 总结 前言 在C11标准中&#xff0c;引入了一系列强大的算法函数&#xff0c;用于处理容器和序列。这些算法函…

将网站域名访问从http升级到https(腾讯云/阿里云)

文章目录 1.前提说明2.服务器安装 docker 与 nginx2.1 安装 docker&#x1f340; 基于 centos 的安装&#x1f340; 基于ubuntu 2.2 配置阿里云国内加速器&#x1f340; 找到相应页面&#x1f340; 创建 docker 目录&#x1f340; 创建 daemon.json 文件&#x1f340; 重新加载…

MATLAB算法实战应用案例精讲-【图像处理】SLAM技术详解(最终篇)

目录 前言 知识储备 点云数据 传感器 3D视觉方案 几个高频面试题目

Linux命令(99)之rz

linux命令之rz 1.rz介绍 linux命令rz是用来把文件从windows等平台上传到Linux上 2.rz用法 rz [参数] file rz参数 参数说明-b使用binary的方式上传&#xff0c;不解释字符为ascii-y相同文件名&#xff0c;覆盖-E相同文件名&#xff0c;不会将其覆盖&#xff0c;而是会在所上…

小程序如何设置各种时间参数

在小程序管理员后台->基本设置处&#xff0c;可以设置各种时间。例如待支付提醒时间、待支付取消时间、自动发货时间、自动收货时间、自动评价时间等等。下面具体解释一下各个时间的意思。 1. 待支付提醒时间&#xff1a;在用户下单后&#xff0c;如果一段时间内没有完成支付…

一些概念梳理

MinGW // 适用于32位和64位Windows的GCC和LLVM的完整运行时环境 A complete runtime environment for GCC & LLVM for 32 and 64 bit Windows.MinGW,a contraction of "Minimalist GNU for Windows", is a minimalist development environment for native Micro…