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

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

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

[[325704]]

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

线程的状态

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

 

  1. // 为了节约空间,我删除了注释 
  2. public enum State { 
  3.        NEW,//初始状态 
  4.        RUNNABLE,//运行状态 
  5.        BLOCKED,// 阻塞状态 
  6.        WAITING,//等待状态 
  7.        TIMED_WAITING,//超时等待状态 
  8.        TERMINATED;//终止状态 
  9.  } 

这 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,源码如下:

 

  1. /** 
  2.   * The minimum priority that a thread can have. 
  3.   */ 
  4.  public final static int MIN_PRIORITY = 1; 
  5.  
  6. /** 
  7.   * The default priority that is assigned to a thread. 
  8.   */ 
  9.  public final static int NORM_PRIORITY = 5; 
  10.  
  11.  /** 
  12.   * The maximum priority that a thread can have. 
  13.   */ 
  14.  public final static int MAX_PRIORITY = 10; 

线程的创建方式

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

1、继承 Thread,成为 Thread 的子类

 

  1. public class MyThread extends Thread{ 
  2.     @Override 
  3.     public void run() { 
  4.         System.out.println("我是通过继承 Thread 类实现的~"); 
  5.     } 
  6.  
  7.     public static void main(String[] args) { 
  8.         MyThread thread = new MyThread(); 
  9.         // 启动线程 
  10.         thread.start(); 
  11.     } 

2、实现 Runnable 接口

 

  1. public class MyThread1 { 
  2.     public static void main(String[] args) { 
  3.         Thread thread = new Thread(new Runnable() { 
  4.             @Override 
  5.             public void run() { 
  6.                 System.out.println("我是通过 runnable 方式实现的~"); 
  7.             } 
  8.         }); 
  9.         // 启动线程 
  10.         thread.start(); 
  11.     } 

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

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

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

 

  1. public synchronized void start() { 
  2.     /** 
  3.      * This method is not invoked for the main method thread or "system" 
  4.      * group threads created/set up by the VM. Any new functionality added 
  5.      * to this method in the future may have to also be added to the VM. 
  6.      * 
  7.      * A zero status value corresponds to state "NEW"
  8.      */ 
  9.      // 没有初始化,抛出异常 
  10.     if (threadStatus != 0) 
  11.         throw new IllegalThreadStateException(); 
  12.  
  13.     /* Notify the group that this thread is about to be started 
  14.      * so that it can be added to the group's list of threads 
  15.      * and the group's unstarted count can be decremented. */ 
  16.     group.add(this); 
  17.  // 是否启动的标识符 
  18.     boolean started = false
  19.     try { 
  20.      // start0() 是启动多线程的关键 
  21.      // 这里会创建一个新的线程,是一个 native 方法 
  22.      // 执行完成之后,新的线程已经在运行了 
  23.         start0(); 
  24.         // 主线程执行 
  25.         started = true
  26.     } finally { 
  27.         try { 
  28.             if (!started) { 
  29.                 group.threadStartFailed(this); 
  30.             } 
  31.         } catch (Throwable ignore) { 
  32.             /* do nothing. If start0 threw a Throwable then 
  33.               it will be passed up the call stack */ 
  34.         } 
  35.     } 

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

 

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

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

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

 

  1. private native void start0(); 

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

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

 

 

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

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

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

责任编辑:华轩 来源: 互联网平头哥
相关推荐

2022-02-15 07:03:04

start 源码run线程

2022-09-05 15:36:47

线程方法Java

2020-12-15 07:36:12

线程Start Run

2013-03-25 10:14:18

NginxApache

2019-04-19 11:56:48

框架AI开发

2010-03-16 09:19:22

Java多线程

2009-08-26 16:58:12

调用C# Thread

2021-08-14 09:04:58

TypeScriptJavaScript开发

2012-10-10 16:52:21

CentOSDebianUbuntu

2021-10-30 19:57:00

HTTP2 HTTP

2009-06-29 18:08:51

Java多线程join方法

2020-09-15 09:23:19

C++WindowsC#

2020-06-02 14:17:55

QWER排列键盘打印机

2017-09-11 19:58:06

PostgreSQLMySQL数据库

2009-06-29 18:32:52

Java多线程Synchronize

2015-09-10 09:30:54

Java多线程同步

2009-04-27 13:15:04

多线程方法run()

2021-02-26 05:30:25

元素For-Each代码

2023-11-02 08:20:54

SocketZygoteAndroid

2021-03-26 11:50:28

Linuxexals
点赞
收藏

51CTO技术栈公众号