Java 多线程启动为什么调用 start() 方法而不是 run() 方法?

2023-11-12

多线程在工作中多多少少会用到,我们知道启动多线程调用的是 start() 方法,而不是 run() 方法,你知道原因吗?

在探讨这个问题之前,我们先来了解一些多线程的基础知识~

线程的状态

Java 中,定义了 6 种线程状态,在 Thread 类可以找到:

// 为了节约空间,我删除了注释
public enum State {
       NEW,//初始状态
       RUNNABLE,//运行状态
       BLOCKED,// 阻塞状态
       WAITING,//等待状态
       TIMED_WAITING,//超时等待状态
       TERMINATED;//终止状态
 }

这 6 种状态之间的关联,可以看下面这张图:

图片来源网络

这张图描述的还是非常详细的,结合这张图,来说说这几种状态分别代表着什么意思:

  • 1、NEW 表示线程创建成功,但没有运行,在 new Thread 之后,没有 start 之前,线程都处于 NEW 状态;
  • 2、RUNNABLE 表示线程正在运行中,当我们运行 strat 方法,子线程被创建成功之后,子线程的状态变成 RUNNABLE;
  • 3、TERMINATED 表示线程已经运行结束,子线程运行完成、被打断、被中止,状态都会从 RUNNABLE 变成 TERMINATED;
  • 4、BLOCKED 表示线程被阻塞,如果线程正好在等待获得 monitor lock 锁,比如在等待进入 synchronized 修饰的代码块或方法时,会从 RUNNABLE 变成 BLOCKED;
  • 5、 WAITING 和 TIMED_WAITING 都表示等待,现在在遇到 Object#wait、Thread#join、
    LockSupport#park 这些方法时,线程就会等待另一个线程执行完特定的动作之后,才能结
    束等待,只不过 TIMED_WAITING 是带有等待时间的;
优先级

优先级代表线程执行的机会的大小,优先级高的可能先执行,低的可能后执行。

在 Java 源码中,优先级从低到高分别是 1 到 10,线程默认 new 出来的优先级都是 5,源码如下:

 /**
  * The minimum priority that a thread can have.
  */
 public final static int MIN_PRIORITY = 1;

/**
  * The default priority that is assigned to a thread.
  */
 public final static int NORM_PRIORITY = 5;

 /**
  * The maximum priority that a thread can have.
  */
 public final static int MAX_PRIORITY = 10;
线程得创建方式

我们创建多线程有两种方式,一种是继承 Thread 类,另一种是实现 Runnable 接口。两种方式的使用,如下所示:

1、继承 Thread,成为 Thread 的子类
public class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("我是通过继承 Thread 类实现的~");
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        // 启动线程
        thread.start();
    }
}
2、实现 Runnable 接口
public class MyThread1 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("我是通过 runnable 方式实现的~");
            }
        });
        // 启动线程
        thread.start();
    }
}

不管使用哪一种方式,启动线程都是thread.start()方法,如果你做过实验的话,你会发现 thread.run()也可以执行,为什么就一定需要调用thread.start()方法呢

先说说结论:首先通过对象.run()方法可以执行方法,但是不是使用的多线程的方式,就是一个普通的方法,要想实现多线程的方式,一定需要通过对象.start()方法

想要弄明白一个问题,最好的办法就是从源码入手,我们也从这两个方法的源码开始,先来看看 start 方法的源码:

public synchronized void start() {
    /**
     * This method is not invoked for the main method thread or "system"
     * group threads created/set up by the VM. Any new functionality added
     * to this method in the future may have to also be added to the VM.
     *
     * A zero status value corresponds to state "NEW".
     */
     // 没有初始化,抛出异常
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    /* Notify the group that this thread is about to be started
     * so that it can be added to the group's list of threads
     * and the group's unstarted count can be decremented. */
    group.add(this);
	// 是否启动的标识符
    boolean started = false;
    try {
	    // start0() 是启动多线程的关键
	    // 这里会创建一个新的线程,是一个 native 方法
	    // 执行完成之后,新的线程已经在运行了
        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 */
        }
    }
}

start 方法的源码也没几行代码,注释也比较详细,最主要的是 start0() 方法,这个后面在解释。再来看看 run() 方法的源码:

    @Override
    public void run() {
	    // 简单的运行,不会新起线程,target 是 Runnable
        if (target != null) {
            target.run();
        }
    }

run() 方法的源码就比较简单的,就是一个普通方法的调用,这也印证了我们上面的结论。

接下来我们就来说一说这个 start0() 这个方法,这个是真正实现多线程的关键,start0() 代码如下:

private native void start0();

start0 被标记成 native ,也就是本地方法,并不需要我们去实现或者了解,**为什么 start0() 会标记成 native? **

这个要从 Java 跨平台说起,看下面这张图:

图片来源牛客网

start() 方法调用 start0() 方法后,该线程并不一定会立马执行,只是将线程变成了可运行状态。具体什么时候执行,取决于 CPU ,由 CPU 统一调度。

我们又知道 Java 是跨平台的,可以在不同系统上运行,每个系统的 CPU 调度算法不一样,所以就需要做不同的处理,这件事情就只能交给 JVM 来实现了,start0() 方法自然就表标记成了 native。

最后,总结一下,Java 中实现真正的多线程是 start 中的 start0() 方法,run() 方法只是一个普通的方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 多线程启动为什么调用 start() 方法而不是 run() 方法? 的相关文章

  • 【星海随笔】计组数学小课堂

    计算机组成原理 https www bilibili com video BV1ps4y1d73V p 8 16的负一次方既为1 16 16 1 16进制转换为10进制 例如 5 8 5 16 1 8 16 1 十进制转N进制 则除以N 然
  • Transformers中文本生成方法model.generate()参数解释

    本博客仅作为记录 参考 LLM 大语言模型 解码时是怎么生成文本的 爱码网
  • python字符串中所有符合条件的索引

    使用re库中的finditer import re s 1111ah11111ah test re finditer ah s print i for i in test
  • mvp关联activity生命周期_Android MVP架构从入门到精通-真枪实弹

    Android MVP架构从入门到精通 真枪实弹 一 前言 你是否遇到过Activity Fragment中成百上千行代码 完全无法维护 看着头疼 你是否遇到过因后台接口还未写而你不能先写代码逻辑的情况 你是否遇到过用MVC架构写的项目进行
  • VMware Workstation 16 Player 安装Centos 7

    环境准备 VMware Workstation 16 Player 官方下载 https www vmware com products workstation player workstation player evaluation ht
  • Cesium 源码解析 Model(一)

    Cesium中对于3DTiles会解析成Model 特别是3DTile中的B3DM 过程主要是对gltf在Cesium中是如何解析并生成绘制命令的 content model new Model gltf gltfView gltf数据 c
  • 全球数字治理白皮书 附下载

    当今世界 科技革命和产业变革日新月异 数字经济蓬勃发展 深刻改变着人类生产生活方式 对各国经济社会发展 全球治理体系 人类文明进程影响深远 在经济全球化遭遇逆流 保护主义 单边主 义上升的背景下 数字化驱动的新一轮全球化仍蓬勃发展 已成为助
  • QT学习笔记(七)

    第12章 输入与输出 Qt提供了读写字节块的设备类QIODevice QIODevice类是抽象的 无法被实例化 一般是使用它的子类 它包括如下子类 其中 QProcess QTcpSocket QUdpSocket QSslSocket都
  • Java 优先级队列

    文章目录 Java 优先级队列 PriorityQueue简介 继承关系 PriorityQueue示例 Comparable比较器 Comparable接口 Comparator比较器 Comparator接口 底层原理 Java 优先级
  • Unity3d 游戏优化 mono等

    GC问题 1 C 变量分为两种类型 值类型和引用类型 值类型分配在栈区 引用类型分配在堆区 GC关注引用类型 2 GC卡顿原因 堆内存垃圾回收 向系统申请新的堆内存 3 GC触发条件 堆内存分配而当内存不足时 按频率自动触发 手动强行触发
  • 【Vue】 前端上传图片时限制只可以按文件夹上传图片( webkitdirectory )

    前言 最近再对公司前后端不分离的 C Winform 系统进行 Java Web 重构 为了保持客户使用习惯所以要高度还原 其中有一个功能就是上传图片时 以文件夹进行上传 要读取文件夹内所有的图片 看了挺多大神的博客 也看了 Element
  • Keepalived与HaProxy的协调合作原理分析

    Keepalived与HaProxy的协调合作原理分析 keepalived与haproxy合作场景 更好的理解方式 协调合作中考虑的问题 一 Keepalived 以TCP IP模型角度来分析 二 HaProxy 总结 协调合作中考虑的问
  • Go-Python-Java-C-LeetCode高分解法-第四周合集

    前言 本题解Go语言部分基于 LeetCode Go 其他部分基于本人实践学习 个人题解GitHub连接 LeetCode Go Python Java C Go Python Java C LeetCode高分解法 第一周合集 Go Py
  • 由于找不到d3dx9_43.dll无法继续执行-修复教程

    d3dx9 43 dll是一个非常重要的DLL文件 它属于微软DirectX 9 0c的一部分 它主要用于游戏应用程序的开发和运行 在使用Windows平台的玩家中非常常见 这个文件通过DirectX接口提供了一些相关的功能 比如图像和声音
  • 该服务器因不稳定无法正常反应,服务器连接异常

    服务器连接异常 内容精选 换一换 ELB的常见异常返回码有400 403 502 504等 若遇到这些返回码建议您先直接访问后端云服务器 查看是否是后端云服务器的异常 若后端云服务器响应正常 请参考表1排查处理 如果仍无法解决 请联系客服人
  • 非关系型数据库、关系型数据库mysql简介

    Nosql 非关系数据库 数据存储不需要固定的模式 NoSql特点 易扩展 数据之间无关系 大数据量高性能 细粒度 cache性能高 多样灵活的数据模型 增删字段容易 关系数据库 vs NoSql 传统关系数据库 ACID 事务所具有的四个
  • 【华为OD机试】组成最大数 (C++ Python Java)2023 B卷

    题目描述 小组中每位都有一张卡片 卡片上是6位内的正整数 将卡片连起来可以组成多种数字 计算组成的最大数字 输入描述 号分割的多个正整数字符串 不需要考虑非数字异常情况 小组最多25个人 输出描述 最大的数字字符串 用例1 输入 22 22

随机推荐

  • 机械电子工程用不用学c语言,机械电子工程到底学什么 毕业以后能干什么

    机械电子工程专业俗称机电一体化 是机械工程与自动化的一种 下文有途网小编给大家整理了机械电子工程的就业方向及前景 供参考 机械电子工程专业主要学什么 机械电子工程要学习得课程有 电工与电子技术 机械制图 工程力学 机械设计基础 机械制造基础
  • 红队打靶,红日系列,红日靶场5

    文章目录 靶场详情 外网渗透 端口扫描 漏洞发现与利用 获取shell 内网渗透 提权 内网信息收集 横向移动 第一种使用CS 第二种使用msf 路由转发与代理通道 Psexec 攻击 靶场详情 此次靶场虚拟机共用两个 一个外网一个内网 用
  • java实现二分查找-两种方式

    二分查找是一种查询效率非常高的查找算法 又称折半查找 起初在数据结构中学习递归时实现二分查找 实际上不用递归也可以实现 毕竟递归是需要开辟额外的空间的来辅助查询 本文就介绍两种方法 二分查找算法思想 有序的序列 每次都是以序列的中间位置的数
  • 看融合数学教学的steam教育模式

    传统意义上的 教育 德育 教学双肩挑 是要求教师能够处理好 德育 和 教学 两方面的工作要求 这实质上是通过人为划分的两个途径实现对学生的培养 STEAM教育的跨学科整合思维能够很好的迁移在 德育 与 教学 的整合上 即在教师引导下的学生参
  • OWASP Top 10 2021 榜单出炉,安全人员必看

    作为一个安全人员 如果你还不知道OWASP Top 10是什么 那么请你接着往下看 OWASP 开放式Web应用程序安全项目 是一个开源的 非营利性的全球性安全组织 致力于改进Web应用程序的安全 这个组织最出名是 它总结了10种最严重的W
  • 数据结构小白之队列与环形队列的模拟

    队列的简单介绍 队列是一个有序列表 可以使用数组或者链表来实现 队列遵循先入先出的原则 先存入队列的数据要先取出 后存入的后取出 使用数组来模拟队列 说明 实现思路 示意图 代码 1 说明 队列本身是有序列表 若使用数组的结构来存储队列的数
  • 安装和更新node的正确姿势

    干货时刻 本文主要讲解了如何安装node 以及如何更新node的版本 node js 是什么 简称node 是基于Chrome V8引擎的JavaScript JS 运行时环境 node 安装 进入node 官网 点击如下图所示的安装包即可
  • 前端之HTML

    一 概念 1 页面组成 结构 HTML Hyper Text Markup Language 超文本标记语言 页面原始和内容 表现 CSS 网页原始的外观和位置等页面样式 如颜色 大小等 行为 JavaScript 网页模型的定义与交互 简
  • 计算机组成原理慕课测试-第四单元

    计算机字长32位 主存容量为128MB 按字编址 其寻址范围为 0 32M 1 1 B 8b 1 kB 1024 B kB kilobajt 千 1 MB 1024 kB MB megabajt 兆 1 MB 1024 kB MB mega
  • IPV6 阿里DDNS

    IPV6 阿里DDNS 因为需要在家搭建一套环境 并且需要公网能访问 国内的ipv4的地址 各大运营商基本都不会分配ipv4地址 电信宽带好像有地方可以 但是听说很贵 而且是动态的 每过段时间就会改变 发现移动宽带的公网ipv6地址是可以获
  • 微信为什么更受欢迎?

    想必大家都和我一样 曾是一个QQ的忠实用户 认为QQ是最受欢迎的社交软件 其实不然 微信比QQ更受欢迎 只是我们根本不知道微信 所以 我们来谈谈微信NB在那里吧 1 QQ的用户是年轻化 娱乐性强 而微信让不是qq用户的人也加入进来 变得更加
  • CentOS通过nvm安装管理node

    今天搭建CentOS node 环境 原本打算源码安装 环境编译一直出错 为节省时间 直接用nvm 来下载和管理node nvm 是一个开源软件 大家可以在github 上面 下载它的源码https github com creationi
  • 【AI with ML】第 14 章 :在 iOS 应用程序中使用 TensorFlow Lite

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • Applications(4)

    CONTENTS Other Applications In this section we cover a few other types of applications of deep learning that are differe
  • java使用smb操作win共享文件夹

    package com zky util import jcifs smb SmbException import jcifs smb SmbFile import jcifs smb SmbFileInputStream import j
  • 新手LearnOpenGL纹理不显示的部分解决方法

    项目场景 在LearnOpenGL学习中遇到的一些问题 照着写但是纹理加载不出来或者两张纹理只加载一张 问题描述 lt 纹理加载不出来 gt 1 文件路径是否正确 是否输出texture load fail等提示 设置了成功检查才会有提示
  • RK3568 Camera 使用

    RK3568 Camera 使用 RK3568 Sensor驱动开发移植 1 RK3568 Sensor驱动开发移植 2 RK3568 Sensor驱动开发移植 3 MIPI CSI用法 RK3568平台仅有一个标准物理mipi csi2
  • 修改pip下载源

    pip国内的一些镜像 阿里云 中国科学技术大学 豆瓣 清华大学 中国科学技术大学 修改源方法一 linux 修改 pip pip conf 如无就创建一个新的 修改内容为 global index url https pypi tuna t
  • 三、C语言进阶:二维指针

    3 二维指针 3 1 什么是二维指针 二维指针与一维指针一样都是保存变量的地址 实例 一维指针存放变量地址 二维指针存放一维指针地址 include
  • Java 多线程启动为什么调用 start() 方法而不是 run() 方法?

    多线程在工作中多多少少会用到 我们知道启动多线程调用的是 start 方法 而不是 run 方法 你知道原因吗 在探讨这个问题之前 我们先来了解一些多线程的基础知识 线程的状态 Java 中 定义了 6 种线程状态 在 Thread 类可以