妹子問我為啥啟動線程時使用 start 而不是 run

2020-12-17 51CTO

本文轉載自微信公眾號「Java極客技術」,作者鴨血粉絲。轉載本文請聯繫Java極客技術公眾號。 

今天團隊裡面的妹子問阿粉,為什麼在啟動線程的時候,都使用 start 方法,而不是 run 方法呢

還好阿粉平時一直有在學習,要不真的被妹子問住了

在多線程中,如果想讓一個線程啟動,你使用的方法一定是 thread.start() 方法,而不是 thread.run() 方法(啥,你用的不是 thread.start() 方法?乖,你的打開方式不對哦,下次不要這樣了

有沒有疑惑,為什麼每次我們都習慣調用 start() 方法,為什麼不直接調用 run() 方法來啟動線程呢?

而且如果去看源碼的話,你會發現,在 thread.start() 方法中,其實最後還是調用了 thread.run() 方法來執行

  1. Causes this thread to begin execution; the Java Virtual Machine 
  2.  calls the <code>run</code> method of this thread. 

上面的注釋翻譯一下:當線程開始執行時, JVM 會調用此線程的 run 方法

也就是說,線程的 run 方法是由 JVM 直接調用的,在 Java 中如果我們想要直接調用 run 方法也是可以的,因為在 Java 中 run 方法是 public 的

  1. @Override 
  2.    public void run() { 
  3.        if (target != null) { 
  4.            target.run(); 
  5.        } 
  6.    } 

那既然 start 方法最後調用的也是 run 方法,再加上 run 方法本身支持直接調用,那為啥我們平時寫的程序都是調用 start 方法,而不是 run 方法呢

那是因為,如果直接調用 run 方法,就不是多線程了

為了方便解釋,咱們看個小 demo :

  1. public class RunThread { 
  2.     public static void main(String[] args) { 
  3.         Thread runThread = new Thread(new Runnable() { 
  4.             @Override 
  5.             public void run() { 
  6.                 System.out.printf("Run begin another , 當前線程 : %s.%n" ,Thread.currentThread().getName()); 
  7.             } 
  8.         }); 
  9.  
  10.         // 啟動線程 
  11.         runThread.start(); 
  12.  
  13.         // 直接調用 run 方法  
  14.         runThread.run(); 
  15.  
  16.         System.out.printf("Run begin , 當前線程 : %s.%n" ,Thread.currentThread().getName()); 
  17.     } 

上面的程序運行結果如下:

你會發現, runThread 的 run 方法被執行了兩次

一次是 run 方法運行在自己的線程中,從 Run begin another , 當前線程 : Thread-0 可以看到,這個線程是運行在 Thread-0 中

另外一次是因為我們的程序代碼直接調用了 run 方法,此時的線程運行在 main 線程中,從 Run begin another , 當前線程 : main 可以看出來

也就是說,如果我們直接調用 run 方法的話,線程並不是運行在自己的線程中,而是運行在了當前線程中

我們為什麼要創建多線程?不就是希望多個線程並行執行,比如現在我是線程 A ,此時又起了一個線程,那麼我希望這個線程是和線程 A 一起運行的,如果直接調用了 run 方法的話,就運行在線程 A 裡面來了

並沒有達到創建多線程的目標,這怎麼行呢,對不對

所以在啟動線程時,都是使用 start 方法,而不是 run 方法

這一點,其實在源碼中也有說明:

the Java Virtual Machine calls the run method of this thread.

The result is that two threads are running concurrently:

the current thread (which returns from the call to the start method)

and the other thread (which executes its run method).

在 JVM 調用線程的 run 方法之後,結果就是兩個線程同時運行:

  • 當前線程(從調用返回到 start 方法)
  • 另一個線程(執行 run 方法)

一個線程能不能 start 兩次?

妹子搞懂了為什麼線程一般都使用 start 方法,不使用 run 方法,因為調用的話,就違背了我們想要用多線程去處理的初衷

妹子又問阿粉,那一個線程是不是可以 start 兩次呢?

負責任的阿粉趕緊告訴小妹妹是不可以的

如果一個線程啟動兩次,想都不用想會拋出 IllegalThreadStateException 這個異常

這個錯誤我們也能在源碼中看到:

  1. if (threadStatus != 0) 
  2.     throw new IllegalThreadStateException(); 

線程開始 start 時,會首先判斷 threadStatus 的值是否為 0 ,如果值不是 0 的話,說明這個線程的 state 狀態不是 new 就拋出 IllegalThreadStateException 異常

啊?竟然還想問阿粉,線程的狀態除了 new 還有什麼?阿粉以前寫過一篇,要不要看看:面試官沒想到,一個 Java 線程生命周期,我可以扯半小時

【編輯推薦】

【責任編輯:

武曉燕

TEL:(010)68476606】

點讚 0

相關焦點

  • Python零基礎入門教程,如何使用多線程(上)?
    大綱了解多線程創建多線程有兩種方式守護線程多線程多個任務可以用多進程完成,也可以使用同一進程中的多個線程之間可以並發執行完成創建多線程# 方式一import threadingimport timedef run(name): print('{0} 開始跑步'.format(name))
  • java入門避坑必讀,通過Thread類創建java多線程
    1 為什麼要用多線程平常我們做crud的時候,用到多線程的機會不多。但當我們要處理一些複雜的業務時,或者提高程序處理效率時,就繞不開多線程的使用。也有些時候,我們需要對某個接口進行並發測試,也可以通過多線程來做一個性能測試小程序。
  • 「011期」JavaSE面試題(十一):多線程(1)
    Thread.stop(),不建議使用通過一個變量去控制,當符合這個條件時,自動結束。方式一:繼承 Thread 類Thread 本質上也是實現了 Runnable 接口的一個實例,它代表一個線程的實例,並且,啟動線程的唯一方法就是通過 Thread 類的 start()實例方法。
  • Java 多線程基礎 interrupt()和線程終止方式
    二、線程終止方式Thread中的 stop() 和 suspend() 方法,由於固有的不安全性,已經建議不再使用!下面,我先分別討論線程在「阻塞狀態」和「運行狀態」的終止方式,然後再總結出一個通用的方式。(一)、終止處於「阻塞狀態」的線程.通常,我們通過「中斷」方式終止處於「阻塞狀態」的線程。
  • 淺談Java線程狀態轉換及控制
    在Java中可以使用stop 方法停止一個線程,使該線程進入死亡狀態。但是使用這種方法結束一個線程是不安全的,在編寫程序時應當禁止使用這種方法。所以在編寫程序時,應儘量避免使用suspend,如確實需要阻塞一個線程的運行,最好使用wait方法,這樣既可以阻塞掉當前正在執行的線程,同時又使得該線程不至於陷入死鎖。  用一句話說就是:stop方法是線程不安全的,可能產生不可預料的結果;suspend方法可能導致死鎖。如何終止一個線程?
  • 實現多線程的標準操作,基於Runnable接口實現java多線程
    MyThread2,添加一個run方法,這是業務核心。本次線程依然是實現了一個從1到100的報數功能。3 創建並啟動線程我們通過new Thread的方式創建3個先出對象。把MyThread2作為參數,傳入剛創建的Thread對象中。
  • 線程不是你想中斷就能中斷
    為什麼不強制停止 如何用 interrupt 停止線程 sleep 期間能否感受到中斷 停止線程的方式有幾種 總結啟動線程需要調用 Thread 類的 start() 方法,並在 run() 方法中定義需要執行的任務。啟動一個線程非常簡單,但如果想要正確停止它就沒那麼容易了。
  • 新手編程:Java多線程中Thread與Runnable的區別
    Java多線程中Thread與Runnable的區別定義extends Thread子類繼承Thread具備多線程能力,可以實現多線程;啟動線程的方法:①創建子類對象②對象名.start();不建議使用:避免多線程OOP單繼承的局限性(OOP:Object Oriented Programming,面向對象的編程、類似的還有OOD(面向對象的設計),OOA(面向對象的分析));implements Runnable
  • 程式設計師:還不知道怎么正確地停止線程?
    例如用戶行為的一個取消動作,運行超時或者運行過程中出現錯誤也是需要停止當前線程,應用程式需要進行重啟等都需要我們主動的停止正在運行的線程,如何安全可靠的停止線程並不是一件簡單的事情。java語言並沒有一種機制可以安全可靠的去停止一個線程,但是提供了一個中斷(interrupt)機制,是通過啟動一個線程去通知當前真正運行的線程,告訴它你別運行了,可以停止了。而不是強者進行停止。
  • Java項目實踐,CountDownLatch實現多線程閉鎖
    摘要本文主要介紹Java多線程並發中閉鎖(Latch)的基本概念、原理、示例代碼、應用場景,通過學習,可以掌握多線程並發時閉鎖(Latch)的使用方法。概念「閉鎖」就是指一個被鎖住了的門將線程a擋在了門外(等待執行),只有當門打開後(其他線程執行完畢),門上的鎖才會被打開,a才能夠繼續執行。
  • Java之Thread類的常用方法之獲取線程名稱
    獲取當前線程的名稱有兩種方法1.使用Thread類中的方法getName(),String getName()返回該線程的名稱。2.可以獲取當前正在執行的線程,使用線程中的方法getName()獲取線程的名稱。static Thread current Thread()返回對當前正在執行的線程對象的引用。
  • Android啟動頁面延遲跳轉與第二次啟動分支跳轉
    在打開一個App時,我們第一眼看到的往往是一個閃屏頁面,之所以叫閃屏頁,是因為它出現之後會短暫地停留幾秒鐘再跳轉到其他頁面。閃屏頁除了使用戶體驗更好外,還能給app留出初始化數據的時間。下面,是我總結的閃屏頁的三種寫法。
  • Thread類的常用方法之設置線程名稱
    如何設置線程名稱呢?有兩種方法。使用Thread類中的方法setName(名字)。void setName(String name)改變線程名稱,使之與參數name相同。2.創建一個帶參數的構造方法,參數傳遞線程的名稱。調用父類的帶參構造方法,把線程的名稱傳遞給父類,讓父類(Thread)給子線程起一個名字Thread(String name)分配新的Thread對象。第一種方法,使用Thread類中的方法setName(名字)。
  • Java多線程:帶你了解神秘的線程變量 ThreadLocal
    前言在 Java多線程中,線程變量ThreadLocal非常重要,但對於很多開發者來說,這並不容易理解,甚至覺得有點神秘今天,我將獻上一份 ThreadLocal的介紹 & 實戰攻略,();            new Thread(runnable, "線程1").start();            new Thread(runnable, "線程2").start();        }                public static class MyRunnable implements Runnable {
  • Python並發編程很簡單,一文幫你搞清如何創建線程類
    對於Python的並發編程相關的東東,相信通過上次咱們的探討,大家已經比較清楚了,對於Python創建線程的方式主要有兩種,這個上次咱們也已經說過了哦,第一種是使用threading模塊的Thread類的構造器來創建線程,這種方式上次咱們已經詳細討論過了哦,這次呢,咱們就重點和大家來聊聊第二種方式吧
  • 通俗易懂的告訴你「策略模式」在java多線程中的應用
    花10分鐘認真的閱讀一篇文章有時或許比敲60分鐘代碼還有效我們都知道java啟動多線程有兩種方式,一種是繼承Thread類,一種是實現Runnable接口,但是很多小夥伴可能不知道實現Runnable接口這種方式中運用了
  • Java 實現線程的方式有幾種方式?帶有返回值的線程怎麼實現?
    編輯2:實現Runnable接口的寫法:定義一個類,實現Runable接口,重新run方法。New個thread對象,使用有參構造器,參數是runnable的。然後thread.start()二:帶有返回值的3:實現Callable<V>接口操作步驟:一個類實現Callable接口,重新call方法;在調用的時候,需要使用FutureTask這個類的有參構造
  • android 主線程 線程優先級 - CSDN
    android可以通過調用thread對象的setPriority方法為線程設置優先級,優先級只能取1~10之間的整數。優先級越大意味著線程優先級越高,在大量並發時享有更多的cpu執行時間。
  • 長跑不是「long run」,短跑也不是「short run」!正確的表達是...
    the long run = 從長遠看「長跑」的正確表達是:long-distance runninga long-distance runner = 長跑選手例:As a long-distance runner, she's in a class of her own.
  • 面試官問我:線程池中多餘的線程是如何回收的?
    不過,我倒是對線程池是如何回收工作線程比較感興趣,所以簡單分析了一下,加深對線程池的理解吧。下面以JDK1.8為例進行分析1. runWorker(Worker w)工作線程啟動後,就進入runWorker(Worker w)方法。