多线程在工作中多多少少会用到,我们知道启动多线程调用的是 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() 方法只是一个普通的方法。

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

  1. java中多线程执行时,为何调用的是start()方法而不是run()方法

    Thead类中start()方法和run()方法的区别 1,start()用来启动一个线程,当调用start()方法时,系统才会开启一个线程,通过Thead类中start()方法来启动的线程处于就绪状 ...

  2. 最简单的java多线程代码(重写thread或者runnable的run方法)

    http://blog.csdn.net/testcs_dn/article/details/42526549 java线程使用示例——最简单的线程 线程使用示例一: [java] view plai ...

  3. java多线程并发去调用一个类的静态方法安全性探讨

    java多线程并发去调用一个类的静态方法安全性探讨 转自:http://blog.csdn.net/weibin_6388/article/details/50750035   这篇文章主要讲多线程对 ...

  4. Java中是否可以调用一个类中的main方法?

    前几天面试的时候,被问到在Java中是否可以调用一个类中的main方法?回来测试了下,答案是可以!代码如下: main1中调用main2的主方法 package org.fiu.test; impor ...

  5. “全栈2019”Java多线程第三十章:尝试获取锁tryLock()方法详解

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

  6. “全栈2019”Java多线程第十二章:后台线程setDaemon()方法详解

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

  7. “全栈2019”Java多线程第七章:等待线程死亡join()方法详解

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

  8. ajax 执行成功以后返回的数据走的是error方法而不是success方法的问题

    今天在一个功能的时候发现写的ajax的方法执行后台代码成功后返回前台时执行的是error方法而不是success方法,代码如下 jQuery('#form').ajaxSubmit({ type: & ...

  9. C#开发笔记之03-为什么选择IsNotXXX方法而不是IsXXX方法?

    C#开发笔记概述 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/959 访问. 为什么有时候要选择IsNotXXX方法而 ...

随机推荐

  1. 原创zookeeper3.4.6集群安装

    tar -zxvf zookeeper-3.4.6.tar.gz -C /home/hadoop/ vi ~/.bash_profile export ZOOKEEPER_HOME=/home/had ...

  2. JMeter分布式压测实战(2020年清明假期学习笔记)

    一.常用压力测试工具对比 简介:目前用的常用测试工具对比 1.loadrunner 性能稳定,压测结果及颗粒度大,可以自定义脚本进行压测,但是太过于重大,功能比较繁多. 2.Apache ab(单接口 ...

  3. sftp的用法

    linux sftp远程连接命令 sftp -oPort=60001 root@192.168.0.254 使用-o选项来指定端口号. -oPort=远程端口号 sftp> get /var/w ...

  4. testNG groups 分组测试

    testNG的分组通过xml文件<groups>标签和@Test(group="组名")来实现分组 xml中关于分组的详细介绍,通过groups 定义一个组,通过< ...

  5. Springboot:静态资源加载(七)

    WebMvc自动配置: 搜索WebMvcAutoConfiguration自动装配类: 第一种方式通过webjars加载静态资源: https://www.webjars.org(通过maven加载依 ...

  6. Java中集合的嵌套

    集合的嵌套遍历 获取10个1-20之间的随机数,要求不能重复 键盘录入多个数据,以0结束,要求在控制台输出这多个数据的最大值. public static void main(String[] arg ...

  7. python信息收集(三)

        前两篇介绍了利用python编写一些脚本实现二层主机的发现,这一篇介绍一下三层主机的发现.     一般来说,三层主机的发现主要是通过ICMP协议来实现的.其中ICMP协议中的ping命令可以 ...

  8. Python代码覆盖率分析工具Coverage

    简介 在测试中,为了度量产品质量,代码覆盖率被作为一种测试结果的评判依据,在Python代码中用来分析代码覆盖率的工具当属Coverage.代码覆盖率是由特定的测试套件覆盖被测源代码的程度来度量,Co ...

  9. RequestDispatcher.forward() 方法和HttpServletResponse.sendRedirect()方法的区别

    RequestDispatcher.forward() 方法和HttpServletResponse.sendRedirect()方法的区别 先贴一段代码 public void logon(Http ...

  10. Python-四则运算-蔡晓晴,杜婷萱

    github链接:https://github.com/Amy-CC/Arithmetic-Operation 一.需求 1.使用-n 参数控制生成题目的个数 2.使用-r 参数控制题目中数值(自然数 ...