start 方法和 run 方法的比较

代码演示:

/** * 

* start() 和 run() 的比较 *

* * @author 踏雪彡寻梅 * @version 1.0 * @date 2020/9/20 - 16:15 * @since JDK1.8 */public class StartAndRunMethod { public static void main(String[] args) { // run 方法演示 // 输出: name: main // 说明由主线程去执行的, 不符合新建一个线程的本意 Runnable runnable = () -> { System.out.println("name: " + Thread.currentThread().getName()); }; runnable.run(); // start 方法演示 // 输出: name: Thread-0 // 说明新建了一个线程, 符合本意 new Thread(runnable).start(); }}复制代码

从以上示例可以分析出以下两点:

  • 直接使用 run 方法不会启动一个新线程。(错误方式)
  • start 方法会启动一个新线程。(正确方式)

start 方法分析

start 方法的含义以及注意事项

  • start 方法可以启动一个新线程。 线程对象在初始化之后调用了 start 方法之后, 当前线程(通常是主线程)会请求 JVM 虚拟机如果有空闲的话来启动一下这边的这个新线程。 也就是说, 启动一个新线程的本质就是请求 JVM 来运行这个线程。 至于这个线程何时能够运行,并不是简单的由我们能够决定的,而是由线程调度器去决定的。 如果它很忙,即使我们运行了 start 方法,也不一定能够立刻的启动线程。 所以说 srtart 方法调用之后,并不意味这个方法已经开始运行了。它可能稍后才会运行,也很有可能很长时间都不会运行,比如说遇到了饥饿的情况。 这也就印证了有些情况下,线程 1 先掉用了 start 方法,而线程 2 后调用了 start 方法,却发现线程 2 先执行线程 1 后执行的情况。 总结: 调用 start 方法的顺序并不能决定真正线程执行的顺序。 注意事项 start 方法会牵扯到两个线程。 第一个就是主线程,因为我们必须要有一个主线程或者是其他的线程(哪怕不是主线程)来执行这个 start 方法,第二个才是新的线程。 很多情况下会忽略掉为我们创建线程的这个主线程,不要误以为调用了 start 就已经是子线程去执行了,这个语句其实是主线程或者说是父线程来执行的,被执行之后才去创建新线程。
  • start 方法创建新线程的准备工作 首先,它会让自己处于就绪状态。 就绪状态指已经获取到除了 CPU 以外的其他资源, 如已经设置了上下文、栈、线程状态以及 PC(PC 是一个寄存器,PC 指向程序运行的位置) 等。 做完这些准备工作之后,就万事俱备只欠东风了,东风就是 CPU 资源。 做完准备工作之后,线程才能被 JVM 或操作系统进一步去调度到执行状态等待获取 CPU 资源,然后才会真正地进入到运行状态执行 run 方法中的代码。
  • 需要注意: 不能重复的执行 start 方法 代码示例 /** *

    * 演示不能重复的执行 start 方法(两次及以上), 否则会报错 *

    * * @author 踏雪去寻梅 * @version 1.0 * @date 2020/9/20 - 16:47 * @since JDK1.8 */ public class CantStartTwice { public static void main(String[] args) { Runnable runnable = () -> { System.out.println("name: " + Thread.currentThread().getName()); }; Thread thread = new Thread(runnable); // 输出: name: Thread-0 thread.start(); // 输出: 抛出 java.lang.IllegalThreadStateException // 即非法线程状态异常(线程状态不符合规定) thread.start(); } } 复制代码 报错的原因 start 一旦开始执行,线程状态就从最开始的 New 状态进入到后续的状态,比如说 Runnable,然后一旦线程执行完毕,线程就会变成终止状态,而终止状态永远不可能再返回回去,所以会抛出以上异常,也就是说不能回到初始状态了。这里描述的还不够清晰,让我们来看看源码能了解的更透彻。

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 方法        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 */        }    }}复制代码

源码中的流程

第一步: 启动新线程时会首先检查线程状态是否为初始状态, 这也是以上抛出异常的原因。即以下代码:

if (threadStatus != 0)    throw new IllegalThreadStateException();复制代码

其中 threadStatus 这个变量的注释如下,也就是说 Java 的线程状态最初始(还没有启动)的时候表示为 0:

/* Java thread status for tools, * initialized to indicate thread 'not yet started' */private volatile int threadStatus = 0;复制代码

第二步: 将其加入线程组。即以下代码:

group.add(this);复制代码

第三步: 最后调用 start0() 这个 native 方法(native 代表它的代码不是由 Java 实现的,而是由 C/C++ 实现的,具体实现可以在 JDK 里面看到,了解即可), 即以下代码:

boolean started = false;try {    // 第三步, 调用 start0 方法    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 */    }}复制代码

run 方法分析

run 方法源码分析

@Overridepublic void run() {    // 传入了 target 对象(即 Runnable 接口的实现), 执行传入的 target 对象的 run 方法    if (target != null) {        target.run();    }}复制代码

对于 run 方法的两种情况

  • 第一种: 重写了 Thread 类的 run 方法,Thread 的 run 方法会失效, 将会执行重写的 run 方法。
  • 第二种: 传入了 target 对象(即 Runnable 接口的实现),执行 Thread 的原有 run 方法然后接着执行 target 对象的 run 方法。
  • 总结: run 方法就是一个普通的方法, 上文中直接去执行 run 方法也就是相当于我们执行自己写的普通方法一样,所以它的执行线程就是我们的主线程。 所以要想真正的启动线程,不能直接调用 run 方法,而是要调用 start 方法,其中可以间接的调用 run 方法。

作者:踏雪欲寻梅
链接:https://juejin.im/post/6877152472512036877
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

线程启动语句的顺序是否决定线程的执行次序。_详细分析 Java 中启动线程的正确和错误方式相关推荐

  1. 线程组多次调用_详细分析 Java 中启动线程的正确和错误方式

    start 方法和 run 方法的比较 代码演示: /** *  * start() 和 run() 的比较  *  *  * @author 踏雪彡寻梅  * @version 1.0  * @da ...

  2. 不允许使用java方式启动_细品 Java 中启动线程的正确和错误方式

    细品 Java 中启动线程的正确和错误方式 前文回顾详细分析 Java 中实现多线程的方法有几种?(从本质上出发) start 方法和 run 方法的比较 代码演示:/** * * start() 和 ...

  3. java sleep唤醒_详解Java中的线程让步yield()与线程休眠sleep()方法

    Java中的线程让步会让线程让出优先级,而休眠则会让线程进入阻塞状态等待被唤醒,这里我们对比线程等待的wait()方法,来详解Java中的线程让步yield()与线程休眠sleep()方法 线程让步: ...

  4. java线程怎么重启_如何在Java中启动/停止/重启线程?

    10 个答案: 答案 0 :(得分:41) 一旦线程停止,您就无法重新启动它.但是,没有什么可以阻止您创建和启动新线程. 选项1:创建一个新线程,而不是尝试重新启动. 选项2:而不是让线程停止,让它等 ...

  5. java 线程等待_代码分析Java中线程的等待与唤醒

    我们先来看一下实例代码: class ThreadA extends Thread{ public ThreadA(String name) { super(name); } public void ...

  6. java线程提高速度_如何在JAVA中减慢线程速度

    我有这个类,我在其中运行10次for循环.该类实现了Runnable接口.现在在main()中我创建了2个线程.现在两个都将循环运行到10.但我想检查每个线程的循环计数.如果t1超过7,则让它休眠1秒 ...

  7. Java中的线程-1

    支持多线程编程是Java语言不同与其他语言的优点. 1 :什么是线程? 在回答这个问题之前,先要了解什么是进程. 进程是程序在数据集合上的一次执行过程,是资源申请,调度和独立运行的单位.(现在是不是有 ...

  8. Java中的线程基础篇-线程基本概念

    线程的概念.创建方式.生命周期.优缺点 一.基础知识 1. 进程.线程.协程 1.1 进程 1.2 线程 1.3 协程 2. 串行.并发.并行 2.1 串行 2.2 并发 2.3 并行 二.线程的创建 ...

  9. Java中的线程基础知识

    Java中的线程基础知识 1.线程概念 线程是程序运行的基本执行单元.当操作系统(不包括单线程的操作系统,如微软早期的DOS)在执行一个程序时,会在系统中建立一个进程,而在这个进程中,必须至少建立一个 ...

最新文章

  1. 不错的流量卡官网html源码
  2. Python——编码风格建议
  3. 轻松在阿里云上搭建Redis
  4. 用Java动态代理实现AOP
  5. 像人类一样理解言外之意,阿里AI最新研究成果被国际顶会收录
  6. ad10搜索快捷键_AD 常用快捷键
  7. HEIC图片如何批量转换成jpg格式
  8. mysql 最左原则是什么意思_数据库中的“紧左原则”是什么意思?
  9. Magento数据库结构:EAV (转)
  10. 高德地图+Echarts+Vue
  11. abaqus钢结构螺栓连接分析
  12. win10升级后蓝牙不见了,设备管理器里没有,多了个串行控制器里的未知USB设备?...
  13. 罗克韦尔自动化收购工业自动化系统模拟与仿真的领先软件开发商Emulate3D
  14. PAC模式与全局模式的区别
  15. 不花钱 三分钟就可以搭建商城的详细步骤
  16. 股神巴菲特的成功之道
  17. 重磅:DDN携手潭州学院,全面开启区块链3.0——人才输出、应用落地时代!
  18. Python3:pygame模块的简单的使用(加载图片并让图片动起来)
  19. 偷偷告诉你,十大有效的推广引流渠道有哪些?
  20. FreeRTOS记录(四、FreeRTOS任务堆栈溢出问题和临界区)

热门文章

  1. macos brew zookeeper,安装后zookeeper启动失败?
  2. Docker中Maven私服的搭建
  3. Android 图片识别、图像识别
  4. android EditText光标位置,光标样式,EditText限制输入内容,软键盘遮挡的EditText,搜索框,限制输入表情
  5. Centos7 Greenplum6.1开源版本集群部署
  6. 详解html结构之间的各个关系,层级关系(以列表为例)
  7. linux 漏洞数量,Debian Linux被列为过去20年漏洞数量最多的操作系统
  8. 图书推荐(持续更新)
  9. 大二下学期软件工程概论总结
  10. canvas填充规则