Java 中有时需要将线程进入睡眠状态,这时一般我们就会通过 Thread.sleep 使线程进入睡眠状态,接下去就看看执行该语句在 JVM 中做了什么。

简单例子

以下是一个简单的例子,使主线程睡眠5秒钟。

public class TestSleep {

    public static void main(String[] args) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} }

JVM 中的线程

在继续往 JVM 层看 start0 本地方法前,我们先了解下 JVM 中的相关线程,这将有助于后面更好理解 Java 层线程与 JVM 中的线程对应关系。

在 JVM 中,也用 C++ 定义了一些 Thread 类,它们的继承结构如下,其中对于 Java 层线程到 JVM 层主要相关的有 Java 层的 java.lang.Thread、JavaThread 和 OSThread。

  • java.lang.Thread 属于 Java 层的线程对象,每个 Java 层对象都会在 JVM 中使用 oop 来表示,所以它也会在 JVM 中产生一个 oop。
  • Thread 是 C++ 定义的线程基类,除了 OSThread 类,作为其他线程的基类,它包含了 OSThread 对象的指针。
  • JavaThread 是 C++ 定义的线程类,我们在 Java 层创建的线程对象会使用 JavaThread 对象来表示,它包含了指向线程的 oop 的指针。
  • OSThread 是 C++ 定义的线程,它不与其他线程构成继承关系,它是 JVM 对不同操作系统的线程的统一抽象,它维护了操作系统线程的句柄,用于获取操作系统的线程。
--Thread
--JavaThread
--CodeCacheSweeperThread
--CompilerThread
--JvmtiAgentThread
--ServiceThread
--NamedThread
--ConcurrentGCThread
--VMThread
--WorkerThread
--AbstractGangWorker
--GCTaskThread
--WatcherThread
--OSThread

sleep方法

在 Thread 类中, sleep 是一个静态且本地方法。

public static native void sleep(long millis) throws InterruptedException;

Thread.c

Java 层声明的本地方法对应实现在 Thread.c 中, sleep 是一个注册到 JVM 中的方法,它与 JVM 中的 JVM_Sleep 函数绑定了,所以实现逻辑在 JVM_Sleep 函数里。逻辑为:

  • JVMWrapper("JVM_Sleep") 用于调试。
  • 睡眠时间不能为负。
  • 是否已经被中断了。
  • JavaThreadSleepState jtss(thread) 用于修改线程状态并做一些统计,当睡眠结束后,会修改回线程状态,在 JavaThreadSleepState 的析构函数中修改。
  • 睡眠时间如果为0,则根据 ConvertSleepToYield 做不同处理,它表示是否将 sleep 操作转为 yield 操作。分别调用 os::naked_yield 和 os::sleep 处理,封装了不同操作系统的调用实现,后面以 Windows 为例分别看相应实现。
  • 通过 thread->osthread()->get_state() 获取 OSThread 对象,并将其状态设置为 SLEEPING等到 sleep 结束后设置回原来的状态。
  • 如果睡眠时间大于0,则做类似操作,不过它支持中断。
  • 发送事件,结束。
JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
JVMWrapper("JVM_Sleep"); if (millis osthread()->get_state();
thread->osthread()->set_state(SLEEPING);
os::sleep(thread, MinSleepInterval, false);
thread->osthread()->set_state(old_state);
}
} else {
ThreadState old_state = thread->osthread()->get_state();
thread->osthread()->set_state(SLEEPING);
if (os::sleep(thread, millis, true) == OS_INTRPT) {
if (!HAS_PENDING_EXCEPTION) {
if (event.should_commit()) {
event.set_time(millis);
event.commit();
}
HOTSPOT_THREAD_SLEEP_END(1);
THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
}
}
thread->osthread()->set_state(old_state);
}
if (event.should_commit()) {
event.set_time(millis);
event.commit();
}
HOTSPOT_THREAD_SLEEP_END(0);
JVM_END

os::naked_yield

naked_yield 函数的实现很简单,就直接调用 SwitchToThread 系统函数。通过该函数可以让系统查看是否有其他线程迫切需要CPU,将CPU让给其他线程,如果没有其他线程则立即返回。

void os::naked_yield() {
SwitchToThread();
}

os::sleep

  • 获取最大限制大小limit。
  • 如果超过 limit 则通过减法将其转成多次递归调用 sleep 函数。
  • 获取 OSThread 对象,然后通过 OSThreadWaitState 设置线程状态为等待,修改操作分别在构造函数和析构函数中实现。
  • 根据是否支持中断做不同实现,不需要中断则直接调用 Sleep 系统函数来实现。
  • 如果要支持中断则接着做下面处理。
  • ThreadBlockInVM 主要是检查当前线程用不用进入 safepoint,后面再详细看。
  • 接着主要到 WaitForMultipleObjects 系统函数,该函数能等待指定对象指定的毫秒数。如果等待过程中对象没有接到任何信号,则超过指定毫秒数后返回 WAIT_TIMEOUT ,如果等待过程中对象收到信号,则提前解除等待,此时返回的值为 OS_INTRPT ,即表示被中断了。
int os::sleep(Thread* thread, jlong ms, bool interruptable) {
jlong limit = (jlong) MAXDWORD; while (ms > limit) {
int res;
if ((res = sleep(thread, limit, interruptable)) != OS_TIMEOUT) {
return res;
}
ms -= limit;
} assert(thread == Thread::current(), "thread consistency check");
OSThread* osthread = thread->osthread();
OSThreadWaitState osts(osthread, false /* not Object.wait() */);
int result;
if (interruptable) {
assert(thread->is_Java_thread(), "must be java thread");
JavaThread *jt = (JavaThread *) thread;
ThreadBlockInVM tbivm(jt); jt->set_suspend_equivalent();
HANDLE events[1];
events[0] = osthread->interrupt_event();
HighResolutionInterval *phri=NULL;
if (!ForceTimeHighResolution) {
phri = new HighResolutionInterval(ms);
}
if (WaitForMultipleObjects(1, events, FALSE, (DWORD)ms) == WAIT_TIMEOUT) {
result = OS_TIMEOUT;
} else {
ResetEvent(osthread->interrupt_event());
osthread->set_interrupted(false);
result = OS_INTRPT;
}
delete phri;
jt->check_and_wait_while_suspended();
} else {
assert(!thread->is_Java_thread(), "must not be java thread");
Sleep((long) ms);
result = OS_TIMEOUT;
}
return result;
}

ThreadBlockInVM

前面说到 ThreadBlockInVM 会检查当前线程用不用进入 safepoint,它主要的逻辑如下:

  • 首先设置 Java 线程状态,将状态加一,由 _thread_in_vm = 6 变为 _thread_in_vm_trans = 7,从“运行vm本身代码”到“相应的过度状态”。
  • os::is_MP() 用于判断计算机系统是否为多核系统,多核情况下需要做内存屏障处理,这是为了让每个线程都能实时同步状态。
  • 内存屏障有两种方式,一种是 rderAccess::fence() ,它的实现是直接通过CPU指令来实现,汇编指令为 __asm__ volatile ("lock; addl $0,0(%%rsp)" : : : "cc", "memory"); ,这种方式代价比较大。而另外一种为 InterfaceSupport::serialize_memory ,由 JVM 模拟实现,效率高一点。
  • 调用 SafepointSynchronize::block 尝试在该安全点进行阻塞。
  • 设置 Java 线程状态为 _thread_blocked ,即阻塞。
static inline void transition_and_fence(JavaThread *thread, JavaThreadState from, JavaThreadState to) {
assert(thread->thread_state() == from, "coming from wrong thread state");
assert((from & 1) == 0 && (to & 1) == 0, "odd numbers are transitions states");
thread->set_thread_state((JavaThreadState)(from + 1)); if (os::is_MP()) {
if (UseMembar) {
OrderAccess::fence();
} else {
// Must use this rather than serialization page in particular on Windows
InterfaceSupport::serialize_memory(thread);
}
} if (SafepointSynchronize::do_call_back()) {
SafepointSynchronize::block(thread);
}
thread->set_thread_state(to); CHECK_UNHANDLED_OOPS_ONLY(thread->clear_unhandled_oops();)
}

原文链接:https://juejin.im/post/5b7a0bbbf265da437b082793

从Java到JVM到OS线程睡眠的更多相关文章

  1. Java中JVM内存结构

    Java中JVM内存结构 线程共享区 方法区: 又名静态成员区域,包含整个程序的 class.static 成员等,类本身的字节码是静态的:它会被所有的线程共享和是全区级别的: 属于共享内存区域,存储 ...

  2. [Java Performance] JVM 线程调优

    调整线程栈空间 当很缺少内存时,能够调整线程使用的内存. 每一个线程都有一个栈,用来记录该线程的调用栈信息.线程中的栈的默认空间是有OS和JVM的版本号决定的: OS 32-bit 64-bit Li ...

  3. Java中线程的使用 (2)-多线程、线程优先级、线程睡眠、让步、阻塞

    Java中线程的使用 (2)-多线程.线程优先级.线程睡眠.让步.阻塞 (一)多线程使用方法 说明:创建每个新的线程,一定要记得启动每个新的线程(调用.start()方法) class Xc3 ext ...

  4. “全栈2019”Java多线程第五章:线程睡眠sleep()方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  5. 浅析java线程和OS线程的关系

    探究java线程和OS线程之间的联系 一.准备工作 1.查看linux创建线程的方法    man pthread_create 根据man的配置可知,pthread_create会创建一个线程,这个 ...

  6. Java学习之二(线程(了解) JVM GC 垃圾回收)

    线程与进程(了解)→JVM→字节码→GC 一.程序 = 算法 + 数据结构(大佬) 二.程序 = 框架 + 业务逻辑(现实) 1.线程与进程.同步与异步 1.1进程是什么? 进程就是操作系统控制的基本 ...

  7. Java中的进程和线程

     Java中的进程与线程 一:进程与线程 概述:几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进程运行时,内部可能包括多个顺序执行流,每个顺序执行流就是 ...

  8. Java中的进程与线程(总结篇)

    详细文档: Java中的进程与线程.rar 474KB 1/7/2017 6:21:15 PM 概述: 几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进 ...

  9. JVM 内部运行线程介绍

    转(http://club.alibabatech.org/article_detail.htm?articleId=4) JVM 内部运行线程介绍 作者:蒋家佳/觉梦(支付宝开发工程师) 浏览量: ...

随机推荐

  1. SpringBoot集成RabbitMQ 从零到一,学会MQ异步和解耦--

    RabbitMQ 概念 RabbitMQ 即一个消息队列,_主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用._RabbitMQ使用的是AMQP协议,它是一种二进制协议.默认启 ...

  2. taro Object(...) is not a function 版本更新后,H5端运行出错

    之前使用taro,版本号1.2.11 会有这样的问题,如下:gitHub找解决的,看到大佬们说更新一下版本就好了,果然更新后,此问题解决OK了. 当然,坑是走不完的,版本也更新挺快的,想着把taro和 ...

  3. 监控服务器配置(三)-----Node_exporter安装配置

    此安装主要是为了监控服务器运行状况 1.下载node_exporter安装包(linux版)到 /opt/minitor/node_exporter . 下载地址:https://download.c ...

  4. 如何解决出现AXIOS的跨域问题:Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

    转载:https://www.cnblogs.com/caimuqing/p/6733405.html 问题描述: 由于restful接口需要在头部header传递两个字段: Content-Type ...

  5. 阿里云服务器配置phpstudy实现域名访问【图文教程】

    首先,运行phpStudy,确保Apache和MySql启动,绿色代表正常启动状态. 然后配置站点域名,打开phpStudy的站点域名管理,1.设置域名(你有的域名,最后需要域名解析):2.设置文件的 ...

  6. python 杨辉三角实现逻辑

    程序输出需要实现如下效果: [1] [1,1] [1,2,1] [1,3,3,1] ...... 方法:迭代,生成器 def triangles() L = [1] while True: yiled ...

  7. oracle异机恢复参考官方文档

    How to use Rman Duplicate on ASM/RAC/OMF/Single Instance (Doc ID 840647.1) How to perform Rman dupli ...

  8. Saliency Detection: A Spectral Residual Approach

    Saliency Detection: A Spectral Residual Approach 题目:Saliency Detection: A Spectral Residual Approach ...

  9. Django ORM 常用字段和参数

    Django ORM 常用字段和参数 一:常用字段 AutoField int自增列,必须填入参数 primary_key=True.当model中如果没有自增列,则自动会创建一个列名为id的列. I ...

  10. MR执行环境有两种

    本地测试环境(windows): 在windows的hadoop目录bin目录有一个winutils.exe 1.在windows下配置hadoop的环境变量 2.拷贝debug工具(winutils ...