Heim  >  Artikel  >  Java  >  Ein genauerer Blick auf die richtigen und falschen Möglichkeiten, Threads in Java zu starten

Ein genauerer Blick auf die richtigen und falschen Möglichkeiten, Threads in Java zu starten

coldplay.xixi
coldplay.xixinach vorne
2020-09-28 16:55:092101Durchsuche

Ein genauerer Blick auf die richtigen und falschen Möglichkeiten, Threads in Java zu starten

Schauen wir uns die richtigen und falschen Methoden zum Starten von Threads in Java genauer an das Wesentliche)

Startmethode und Ausführungsmethode Vergleichen Sie
  1. Code-Demonstration:

/**
 * <p>
 * start() 和 run() 的比较
 * </p>
 *
 * @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();
    }
}复制代码

Aus dem obigen Beispiel können wir die folgenden zwei Punkte analysieren:

Verwenden Sie die Methode run direkt wird kein neuer Thread eröffnet. (falscher Weg)

  • start-Methode startet einen neuen Thread. (Richtige Vorgehensweise)

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

    • 代码示例

      /**
      * <p>
      * 演示不能重复的执行 start 方法(两次及以上), 否则会报错
      * </p>
      *
      * @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&#39;s list of threads
     * and the group&#39;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 &#39;not yet started&#39;
 */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 方法,Threadrun 方法会失效, 将会执行重写的 run

Methodenanalyse starten

Bedeutung und Vorsichtsmaßnahmen der Methode starten

🎜 🎜🎜 Die Methode start kann einen neuen Thread starten. 🎜🎜🎜Nachdem das Thread-Objekt nach der Initialisierung die Methode start aufruft, fordert der aktuelle Thread (normalerweise der Hauptthread) die virtuelle JVM-Maschine auf, diesen neuen Thread zu starten, wenn er frei ist. 🎜🎜Mit anderen Worten: Das Wesentliche beim Starten eines neuen Threads besteht darin, die JVM aufzufordern, diesen Thread auszuführen. 🎜🎜Was den Zeitpunkt betrifft, an dem dieser Thread ausgeführt werden kann, wird nicht einfach von uns entschieden, sondern vom Thread-Scheduler. 🎜🎜Wenn es sehr beschäftigt ist, können wir den Thread möglicherweise nicht sofort starten, selbst wenn wir die Methode start ausführen. 🎜🎜Nachdem die Methode srtart aufgerufen wurde, bedeutet dies nicht, dass diese Methode mit der Ausführung begonnen hat. Es kann sein, dass es erst später oder längere Zeit nicht läuft, beispielsweise bei Hunger. 🎜🎜Dies beweist auch, dass in einigen Fällen Thread 1 zuerst die Methode start aufruft und dann Thread 2 die Methode start aufruft, sich jedoch herausstellt, dass Thread 2 die Methode ausführt Thread zuerst 1 nach der Ausführung. 🎜🎜Zusammenfassung: Die Reihenfolge, in der die Methode start aufgerufen wird, bestimmt nicht die Reihenfolge, in der die tatsächlichen Threads ausgeführt werden. Die 🎜🎜🎜Notes🎜🎜🎜start-Methode umfasst zwei Threads. 🎜🎜Der erste ist der Hauptthread, denn wir müssen einen Hauptthread oder andere Threads haben (auch wenn es nicht der Hauptthread ist), um diese start-Methode auszuführen, und der zweite ist der neue Faden. 🎜🎜In vielen Fällen wird der Hauptthread, der den Thread für uns erstellt, ignoriert. Denken Sie nicht fälschlicherweise, dass der Aufruf von start bedeutet, dass er bereits vom untergeordneten Thread ausgeführt wird Zur Ausführung wird ein neuer Thread erstellt. 🎜🎜🎜🎜🎜start-Methode zum Erstellen einer neuen Thread-Vorbereitung 🎜🎜🎜Zuerst wird es sich selbst in den Bereitschaftszustand versetzen. 🎜🎜Der Bereitschaftsstatus bedeutet, dass neben der CPU auch andere Ressourcen abgerufen wurden, z. B. Kontext, Stapel, Thread-Status und PC (PC ist ein Register, PC zeigt auf den Speicherort, an dem das Programm ausgeführt wird) usw. wurden festgelegt . 🎜🎜🎜Nach Abschluss dieser Vorbereitungen ist alles bereit und alles, was wir brauchen, ist Dongfeng, die CPU-Ressource. 🎜🎜Nach Abschluss der Vorbereitungsarbeiten kann der Thread von der JVM oder dem Betriebssystem weiter in den Ausführungsstatus versetzt werden, um auf CPU-Ressourcen zu warten, und dann tatsächlich in den Ausführungsstatus wechseln, um den Code im run-Methode. 🎜🎜🎜🎜🎜Hinweis: Die Startmethode kann nicht wiederholt ausgeführt werden🎜🎜🎜🎜🎜Codebeispiel🎜rrreee🎜🎜🎜Der Grund für den Fehler🎜🎜🎜<code>start Sobald die Ausführung beginnt, Der Thread-Status Geben Sie einfach den nachfolgenden Status aus dem anfänglichen neuen Status ein, z. B. Runnable. Sobald der Thread die Ausführung abgeschlossen hat, wechselt der Thread in den beendeten Status und der beendete Status kann niemals zurückkehren, sodass die obige Ausnahme ausgelöst wird , das heißt Es heißt, dass es nicht in den Ausgangszustand zurückversetzt werden kann. Die Beschreibung hier ist nicht klar genug. Schauen wir uns den Quellcode an, um es besser zu verstehen. 🎜🎜🎜

Analyse des Quellcodes der Methode starten

Quellcode h4>rrreee

Prozess im Quellcode

🎜🎜Schritt 1:🎜Beim Starten eines neuen Threads wird zunächst geprüft, ob der Thread-Status der Ausgangszustand ist. Aus diesem Grund wird die obige Ausnahme ausgelöst. Das heißt, der folgende Code:🎜rrreee🎜Die Variable threadStatus wird wie folgt kommentiert, was bedeutet, dass der Java-Thread-Status zunächst als 0 (noch nicht gestartet) ausgedrückt wird:🎜rrreee🎜🎜Zweiter Schritt: 🎜Fügen Sie es zur Thread-Gruppe hinzu. Das heißt, der folgende Code:🎜rrreee🎜🎜Schritt 3:🎜Rufen Sie schließlich start0() diese native Methode auf (nativ bedeutet, dass ihr Code nicht von Java, sondern von C/C++ implementiert wird, insbesondere The Die Implementierung ist im JDK zu sehen, verstehen Sie es einfach), also den folgenden Code: 🎜rrreee

Methodenanalyse ausführen 🎜

Analyse des Quellcodes der Ausführungsmethode

rrreee

Zwei Situationen für die Ausführungsmethode

🎜🎜🎜Die erste: Umschreiben der Klasse Thread run -Methode wird die run-Methode von Thread ungültig und die überschriebene run-Methode wird ausgeführt. 🎜
  • Zweitens: Übergeben Sie das target-Objekt (d. h. die Implementierung der Runnable-Schnittstelle) und führen Sie den ursprünglichen Lauf von <code>Thread aus. code> Die Methode führt dann die Methode run des Objekts target aus. target 对象(即 Runnable 接口的实现),执行 Thread 的原有 run 方法然后接着执行 target 对象的 run 方法。

  • 总结:

    • run 方法就是一个普通的方法, 上文中直接去执行 run 方法也就是相当于我们执行自己写的普通方法一样,所以它的执行线程就是我们的主线程。
    • 所以要想真正的启动线程,不能直接调用 run 方法,而是要调用 start 方法,其中可以间接的调用 run
  • Zusammenfassung:
      Die run-Methode ist eine gewöhnliche Methode, die der Ausführung der gewöhnlichen Methode entspricht, die wir selbst geschrieben haben Die Methode ist dieselbe, daher ist ihr Ausführungsthread unser Hauptthread.

      Wenn Sie also tatsächlich einen Thread starten möchten, können Sie die Methode run nicht direkt aufrufen, sondern müssen die Methode start aufrufen, in der Sie run Methode.

      Verwandte Lernempfehlungen: 🎜🎜🎜Java-Grundlagen🎜🎜🎜🎜

    Das obige ist der detaillierte Inhalt vonEin genauerer Blick auf die richtigen und falschen Möglichkeiten, Threads in Java zu starten. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Dieser Artikel ist reproduziert unter:juejin.im. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen