Thread类的start()方法创建线程的底层分析

news2024/11/16 19:58:56

在Java中通过如下简单代码就可以创建一个新线程

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        //do something
    }
});
thread.start();

在start()中又调用了start0(),它才是真正创建线程的方法。

public synchronized void start() {
    group.add(this);
    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}

private native void start0();

start0()是一个native方法,这就表明它具体是由C/C++来实现。要查看源代码,需要下载openjdk。openjdk与常用的oralce jdk相比,前者jdk源码开放的更多。官网地址:OpenJDK

 可以选择Mercurial或Github进行下载,此处选择Github

里面提供了很多版本,此处选择jdk8

点击下载即可。

源码下载后,该如何查看呢?针对Thread.java,jdk源码中有一个对应的Thread.c文件

 再回到Thread类,在开头有如下一段代码

private static native void registerNatives();
static {
    registerNatives();
}

它的作用是在创建Thread类的实例时会先调用registerNatives(),也就是调用Thread.c中的Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)。该方法内部又调用RegisterNatives,将Thread类中的方法与JVM方法进行关联,start0对应的JVM方法就是JVM_StartThread。

 JVM_StartThread方法位于hotspot虚拟机里的jvm.cpp,由C++语言编写。

 在该方法里会创建JavaThread对象

JavaThread native_thread = new JavaThread(&thread_entry, sz);

在JavaThread构造方法里调用操作系统的创建线程方法

 支持如下多个系统

 此处选择linux,最终会调用系统函数pthread_create创建线程。这一步就从用户态切换到了内核态,线程创建成功后再从内核态切换到用户态。在Java中不建议频繁创建新线程而是使用线程池做到线程复用,就是因为用户态到内核态的切换会有一定的开销。

int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);

在Linux中通过命令man pthread_create可查看其用法

注:在CentOS中如果提示No manual entry for pthread_create,需要安装包:yum install man-pages libstdc++-docs

该函数的第三个参数表示在线程创建后执行的方法,此处是java_start。第四个参数表示传入java_start方法的参数,从上下文来看,此处是前面创建的JavaThread对象。

接着看java_start方法,还是在os_linux.cpp中,关键代码如下

static void *java_start(Thread *thread) {
  {
    MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);

    // notify parent thread
    osthread->set_state(INITIALIZED);
    sync->notify_all();

    // wait until os::start_thread()
    while (osthread->get_state() == INITIALIZED) {
      sync->wait(Mutex::_no_safepoint_check_flag);
    }
  }

  // call one more level start routine
  thread->run();

  return 0;
}

方法里通过osthread->set_state(INITIALIZED)将内核线程状态置为初始状态,接下来while循环直到状态不等于INITIALIZED就会调用JavaThread对象的run方法。那内核线程状态什么时候会改变呢?这又要回到jvm.cpp中的JVM_StartThread方法,在该方法最后会调用如下方法

Thread::start(native_thread);

该方法会调用os::start_thread(thread)

 os::start_thread(thread)内部先通过JavaThread对象找到对应的内核线程,然后将内核线程状态置为RUNNABLE。

 再次回到上面这张图,while循环条件不满足,终于可以执行下面的thread->run()

static void *java_start(Thread *thread) {
  {
    MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);

    // notify parent thread
    osthread->set_state(INITIALIZED);
    sync->notify_all();

    // wait until os::start_thread()
    while (osthread->get_state() == INITIALIZED) {
      sync->wait(Mutex::_no_safepoint_check_flag);
    }
  }

  // call one more level start routine
  thread->run();

  return 0;
}

 对应thread.cpp里的方法,关键代码如下

void JavaThread::run() {

  // We call another function to do the rest so we are sure that the stack addresses used
  // from there will be lower than the stack base just computed
  thread_main_inner();
}

内部又调用thread_main_inner方法,关键代码this->entry_point()(this, this)

void JavaThread::thread_main_inner() {
  assert(JavaThread::current() == this, "sanity check");
  assert(this->threadObj() != NULL, "just checking");

  // Execute thread entry point unless this thread has a pending exception
  // or has been stopped before starting.
  // Note: Due to JVM_StopThread we can have pending exceptions already!
  if (!this->has_pending_exception() &&
      !java_lang_Thread::is_stillborn(this->threadObj())) {
    {
      ResourceMark rm(this);
      this->set_native_thread_name(this->get_thread_name());
    }
    HandleMark hm(this);
    this->entry_point()(this, this);
  }

  DTRACE_THREAD_PROBE(stop, this);

  this->exit(false);
  delete this;
}

entry_point()返回的值为_entry_point,该值在创建JavaThread对象时进行赋值,是thread_entry方法的指针。在该方法中会调用JavaCalls::call_virtual,通过参数run_method_name可以看出最终调用的是Thread类中的run方法。

总结:当调用Thread对象的start方法后,它的内部调用start0方法。接着通过JNI技术调用虚拟机里用C++编写的方法,在该方法中会创建JavaThread对象,在其构造方法中调用系统函数pthread_create创建内核线程。最终在内核线程中执行Thread对象的run方法。

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

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

相关文章

安全机制(security) - 加解密算法 - 对称加密 - 加解密模式

说明 大部分对称加密算法支持多种加密模式,每种模式的运算结果也不相同。加解密模式是分组加密算法通用的机制,不同算法可能支持相同的加密模式,不同算法支持的加密模式也可能不同。加密和解密需要使用相同的模式才能得到正确的结果。不同的…

CANOE功能介绍

1.CANoe主界面 当计算机安装完CANoe后,用户只需选择“开始”→“所有程序 ”→Vector CANoe 11.0→CANoe 11.0 系 统 菜 单 命 令 即 可 启 动CANoe。 为了快速熟悉CANoe的常用功能,我们可以打开Vector官方的自带例程,一边学习一边实践相关功…

超算/先进计算如何改变现如今对的生活

算力作为新一代的“石油”,与超算/先进计算有着不可分割的紧密联系。 通俗而言,算力泛指计算能力,即数据处理能力。算力大小代表数据处理能力的强弱。从远古的结绳计算到近代的机械式计算,再到现代的数字电子计算,特别…

Ajax学习:设置CROS响应头实现跨域(跨域资源共享)

CROS:跨域资源共享、是官方的跨域解决方案&#xff0c;特点不需要在客户端做任何特殊的操作&#xff0c;完全在服务器中处理&#xff08;支持get post 等&#xff09; 客户端做ajax请求&#xff0c;服务端做相应头设置就可以实现跨域&#xff1a; <!DOCTYPE html> <h…

如何快速构建研发效能度量的指标体系?

本月初&#xff0c;没毛病软件公司的研发总监 Kevin 在参加完公司管理层月度例会后&#xff0c;心情非常糟糕...... 刚才会议中&#xff0c;老板很严肃地问研发总监 Kevin&#xff1a;“我在会议前接到了客户的投诉电话&#xff0c;说产品出现了 Bug&#xff0c;这已经不是第一…

.net-----Windows 窗体应用程序包括控件,对话框,多重窗体,绘制图形,菜单和工具栏

目录前言Windows窗体应用程序概述&#xff1b;窗体和大部分控件常用的事件创建Windows窗体应用程序使用Visual Studio集成开发环境实现Hello World程序使用常用Windows窗体控件&#xff1b;Label、TextBox、RichTextBox、Button应用示例单选按钮、复选框和分组【例】RadioButto…

(附源码)springboot物流配货管理系统 毕业设计 250858

基于springboot物流配货管理系统的设计与实现 摘 要 信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难以攻克的课题.针对物流配货等问题,对物流配货进行研究分析,然后…

电力系统机组组合优化调度(IEEE14节点、IEEE30节点、IEEE118节点)(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f4dd;目前更新&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;电力系统相关知识&#xff0c;期刊论文&…

数云融合丨知识图谱在烟草零售数字化转型中的应用

一、知识图谱的趋势 随着互联网、云计算、大数据、人工智能等信息数据技术的快速发展&#xff0c;计算机的智能化程度也越来越高&#xff0c;知识图谱作为人工智能的核心技术&#xff0c;其在数据集成、语义表示和逻辑推理等方面存在着得天独厚的优势。 2021年&#xf…

Java并发-交替打印的四种方法。

1 前言 如下图所示&#xff0c;现在有两个线程A,B&#xff1b;A打印12345&#xff0c;B打印abcde&#xff0c;结果为1a2b3c4d5e交替输出。 1.1 采用wait和notify 【分析】我们要求线程A始终先打印&#xff0c;因此在线程B先获得CPU使用时间时也应该阻塞。 细节 线程A应该打印…

【人工智能/算法】搜索求解(Solving Problemsby Searching)

文章目录一、求解与搜索二、盲目式搜索1. 深度优先搜索&#xff08;Depth First Search, DFS&#xff09;回溯搜索&#xff08;Backtracking Search&#xff09;2. 广度优先搜索&#xff08;Breadth First Search, BFS&#xff09;一致代价搜索&#xff08;Uniform-cost Search…

TLog轻量级分布式日志标记追踪神器

文章目录TLog简介项目特性安装TLogspringboot依赖spring native依赖日志框架适配方式(举例Log4j框架适配器)任务框架支持(举例XXL-JOB框架)TLog架构图TLog简介 TLog通过对日志打标签完成企业级微服务的日志追踪。它不收集日志&#xff0c;使用简单&#xff0c; 产生全局唯一的…

Actor 生命周期

一&#xff0c;一览图 二&#xff0c; 大致流程 三&#xff0c;细节 从磁盘加载 已位于关卡中的 Actor 使用此路径&#xff0c;如 LoadMap 发生时、或 AddToWorld&#xff08;从流关卡或子关卡&#xff09;被调用时。 包/关卡中的 Actor 从磁盘中进行加载。 PostLoad - 在序…

支持向量机(SVM)—— 详细推导及案例应用可视化

支持向量机&#xff08;SVM&#xff09; 1. 什么是支持向量机&#xff1f; 在上图中&#xff0c;我们想在二维平面中通过画出一条分界线将黑点与白点分开&#xff0c;很明显&#xff0c;我们认为在上面所画的三条分界线中H3H_3H3​是最好的&#xff0c;因为H1H_1H1​压根就没有…

【行为识别】差影法三维人体姿态行为识别【含Matlab源码 277期】

⛄一、简介 该课题为基于MATLAB差影法的人体姿态识别。需要准备对应的模板图片作为背景图&#xff0c;然后测试图和背景图进行作差&#xff0c;结合形态学知识&#xff0c;提取出人体轮廓&#xff0c;接上最外接矩形&#xff0c;得出矩形长宽&#xff0c;计算长宽比例&#xf…

江江文具店铺运营方案设计

目 录 摘 要 I ABSTRACT II 第一章 项目背景 1 1.1电子商务基本概念 1 1.2电子商务现状及发展趋势 1 1.3文具行业的发展现状 2 第二章 文具行业介绍 4 2.1文具行业的调查与分析 4 2.1.1文具行业电子商务渗透率 4 2.1.2文具行业内企业电子商务销售平台使用状况调查统计 5 2.1.3文…

自研芯片重构云上算力革新,满足用户所有负载的需求!

目前&#xff0c;云计算已深入到整个云基础设施&#xff0c;进入硬件协同创新的“深水区”&#xff0c;芯片创新之路是最底层的创新&#xff0c;是最具颠覆性的&#xff0c;也是改变云计算游戏规则的能力。 而亚马逊云科技硬件创新背后的初衷&#xff1a;为云而生的硬件设计&am…

【计算机毕业设计】71.大学生兼职信息系统源码

一、系统截图&#xff08;需要演示视频可以私聊&#xff09; 摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐…

react源码中的协调与调度

requestEventTime 其实在React执行过程中&#xff0c;会有数不清的任务要去执行&#xff0c;但是他们会有一个优先级的判定&#xff0c;假如两个事件的优先级一样&#xff0c;那么React是怎么去判定他们两谁先执行呢&#xff1f; // packages/react-reconciler/src/ReactFibe…

数字化门店| 运动场馆管理系统| 智慧门店小程序

忙碌了一天或闲暇之余想要放松&#xff0c;不少年轻人都会选择前往运动场馆进行各种运动&#xff0c;如打篮球、踢足球、打羽毛球等&#xff0c;一些城市的某个特定区域内聚集着不同品牌的运动场馆&#xff0c;相互之间竞争激烈。 如今&#xff0c;消费升级下&#xff0c;消费者…