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

栏目: IT技术 · 发布时间: 3年前

内容简介:还可以读

互联网平头哥的第  113  篇原创文章 

多线程在工作中多多少少会用到,启动 多线程调用的是 start() 方法,而不是 run() 方法,这是为什么呢?

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

线程的状态

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

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

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

Java 多线程启动为什么调用 start() 方法而不是 run() 方法?
图片来源网络

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

  • 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 跨平台说起,看下面这张图:

Java 多线程启动为什么调用 start() 方法而不是 run() 方法?
图片来源牛客网

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

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

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

还可以读

聊一聊 JUC 下的 ArrayBlockingQueue 队列

聊一聊 JUC 下的 LinkedBlockingQueue

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


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

与孩子一起学编程

与孩子一起学编程

[美] 桑德Warren Sande、Carter Sande / 苏金国、姚曜 等 / 人民邮电出版社 / 2010-11 / 65.00元

一本老少咸宜的编程入门奇书!一册在手,你完全可以带着自己的孩子,跟随Sande父子组合在轻松的氛围中熟悉那些编程概念,如内存、循环、输入和输出、数据结构和图形用户界面等。这些知识一点儿也不高深,听起来备感亲切,书中言语幽默风趣而不失真义,让学习过程充满乐趣。细心的作者还配上了孩子们都喜欢的可爱漫画和经过运行测试的程序示例,教你用最易编写和最易理解的Python语言,写出你梦想中的游戏程序。 ......一起来看看 《与孩子一起学编程》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试