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

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 方法分析

8b6483d6f4c424f44097169d7c225298.png

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
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

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

相关文章

save(),saveOrUpdate(),merge()的区别

save(),saveOrUpdate(),merge()的区别 Save save()方法能够保存实体到数据库,正如方法名称save这个单词所表明的意思。我们能够在事务之外调用这个方法,这也是我不喜欢使用这个方法保存数据的原因。假如两个实体之间有关系(例如employee表和a…

microsoft office 卸载不了

microsoft office (包括常用的office组件,project,visio)等的卸载不是件轻松事,有可能卸载不了,右不会有任何提示,微软也知道自己的东西不好卸载,于是 提供的fix工具。 office 2010…

webgl 游戏_如何选择 WebGL 框架和引擎?

数据可视化Sugar-百度智能云​cloud.baidu.comSugar 是我们从零开始开发的 BI 产品,可以不用写 SQL 制作报表及大屏页面,上半年我们发布了三维场景功能,可以放到大屏中展现:为了实现这个功能,我们调研了大量 WebGL 相关…

Null hypothesis TypeⅠerror Type Ⅱ error

Null hypothesis usually express the phenomenon of no effect or no difference.TypeⅠerror is the incorrect rejection of a true null hypothesis. That is, no difference is taken as difference.Type Ⅱ error is incorrectly retaining a false null hypothesis. Th…

Spring 4 MVC 单元测试例子

首先&#xff0c;要有一个Spring MVC项目&#xff0c;不会的话&#xff0c;点这里看教程。 加入 maven 依赖&#xff1a; <dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version></depend…

java 正则表达式 替换字符串img标签的路径_正则表达式和有限自动机的应用

1. 为什么谈有限自动机时要谈正则表达式&#xff1f;原因是&#xff1a;正则表达式所匹配的所有字符串所构成的语言可以用有限自动机识别。根本原因是&#xff1a;正则集&#xff08;正则表达式所匹配的所有字符串集合&#xff09;是由右线性文法&#xff08;3型文法&#xff0…

Tomcat应用中post方式传参数长度限制

Tomcat应用中post方式传参数长度限制 jsp页面上是没有限制的&#xff0c;但是在tomcat服务器上有限制&#xff0c;Tomcat 默认的post参数的最大大小为2M&#xff0c; 当超过时将会出错&#xff0c;可以配置maxPostSize参数来改变大小。 <Connector port"8080" red…

14.6 Spring MVC 测试框架(翻译)

14.6 Spring MVC 测试框架&#xff08;每天翻译一点点&#xff09; Spring MVC测试框架对 Spring MVC 代码提供一流的测试支持 &#xff0c;它拥有一个 fluent API ,可以和JUnit, TestNG 或其它任何测试框架协同使用。 此测试框架基于 spring-test 模块的Servlet API mock obje…

html读取本地txt_手机本地电子书籍阅读器 — 静读天下

在早期山寨按键手机流行的年代手机中必装两款软件莫过于Anyview和Opera浏览器了&#xff0c;一个是本地最佳阅读工具另一个是压缩流量访问网页的利器&#xff0c;时过境迁好多人都早以忘记了这些昔日的王者。曾经那帮挑灯夜读看小说的熊孩子都已经长大了&#xff0c;在难得的休…

华为手机丢失定位网站_手机端网站优化要从网站的设计和定位开始入手

移动端网站优化要从网站的设计和定位开始入手&#xff0c;重要的是简洁易用&#xff0c;切忌复杂&#xff0c;令用户搞不明白找不到想用的。对于搜索引擎而言&#xff0c;好的用户访问体验也是排名的最佳标准。下面小编就来分析一下设计对手机端网站优化排名的影响。1、使用规范…

Spring MVC 测试 | 模拟提交表单

在 Spring 4 MVC 单元测试例子 一文中利用Spring test 框架进行了简单的测试&#xff0c;代码mockMvc.perform(get("/SayHello/getAnswer"))使用get&#xff08;&#xff09;方法发送了一个GET请求。 现在需求变了&#xff0c;需要提交一个表单&#xff0c;该如何实…

安卓接受后台数据转换解析出错_安卓手机内存越大,速度就会越快?

与非网 2 月 18 日讯&#xff0c;现在安卓手机的内存在产品规划上越来越大&#xff0c;2020 年发布的旗舰手机运行内存已经拓展到惊人的 10GB 以上&#xff0c;而这个运存大小已经超过笔记本主流的 8GB 内存&#xff0c;手机到底需要这么大的内存吗&#xff1f;安卓手机的内存为…

Spring MVC搭建REST风格网站

REST是表述性状态转移的意思。REST核心是以资源为中心。 比如&#xff0c;URI是统一资源标识符&#xff0c;URL是一种URI&#xff0c;称为统一资源定位符。现在很多网站设计的URL,没有以资源为中心&#xff0c;没有体现URI的标识本质。比如&#xff0c;有一个URL&#xff1a;/…

usb

http://blog.csdn.net/jhqin/article/details/6734673 http://www.cnblogs.com/ceachy/archive/2013/03/22/WMI_Usage.html 转载于:https://www.cnblogs.com/answercard/p/5839013.html

ipv6地址格式_IPV6与IPV4的差异

ipv6相比&#xff0c;我们现在的IPV4,有什么优势&#xff0c;有什么差异IPv6的路由表更小,IPv6的组播支持以及对流的支持增强,IPv6加入了对自动配置的支持,IPv6具有更高的安全性. 与IPV4相比&#xff0c;IPV6具有以下几个优势&#xff1a;IPv6具有更大的地址空间。IPv4中规定IP…

@Autowired和构造方法执行的顺序解析

先看一段代码&#xff0c;下面的代码能运行成功吗&#xff1f; Autowiredprivate User user;private String school;public UserAccountServiceImpl(){this.school user.getSchool();} 答案是不能。因为Java类会先执行构造方法&#xff0c;然后再给注解了Autowired 的user注入…

SVN查看提交日志的命令

Windows下&#xff0c;使用svn客户端查看日志很方便&#xff0c;但是如果在linux下&#xff0c;常规查看日志的命令对于日志记录数比较多的时候&#xff0c;屏幕显示不了&#xff0c;就比较麻烦&#xff0c;后来发现了一个很有用的参数可以限制要查看的记录条数 查看某个目录下…

python postgresql跨数据库查询_python访问PostgreSQL数据库之连接库Psycopg2

python访问PostgreSQL数据库之连接库Psycopg2作者&#xff1a;佣工7001由于要在python访问PostgreSQL数据库&#xff0c;需要一个符合DB-API的连接库。通过搜索&#xff0c;锁定两个候选库&#xff1a;一个是py-postgresql&#xff0c;一个是Psycopg2。简单的看了一下py-postgr…

[LeetCode]160. Intersection of Two Linked Lists

题目&#xff1a; Write a program to find the node at which the intersection of two singly linked lists begins. For example, the following two linked lists: A: a1 → a2↘c1 → c2 → c3↗ B: b1 → b2 → b3 判断两个链表是否相交&#x…

MongoClient类参考文档

&#xff08;MongoClient类的实例是&#xff09;一个带有内部连接池的MongoDB客户端。对绝大部分应用来说&#xff0c;都应该有一个MongoClient实例&#xff08;不知怎翻译&#xff09;。 下面各种创建实例的方法是等效的&#xff0c;并且全部都通过默认端口连到本地数据库的&…