深刻淺出線程Thread類的start()方法和run()方法

1、初識

java的線程是經過java.lang.Thread類來實現的。VM啓動時會有一個由主方法所定義的線程。能夠經過建立Thread的實例來建立新的線程。每一個線程都是經過某個特定Thread對象所對應的方法run()來完成其操做的,方法run()稱爲線程體。經過調用Thread類的start()方法來啓動一個線程。html

在Java當中,線程一般都有五種狀態,建立、就緒、運行、阻塞和死亡。java

  第一是建立狀態。在生成線程對象,並無調用該對象的start方法,這是線程處於建立狀態。算法

  第二是就緒狀態。當調用了線程對象的start方法以後,該線程就進入了就緒狀態,可是此時線程調度程序尚未把該線程設置爲當前線程,此時處於就緒狀態。在線程運行以後,從等待或者睡眠中回來以後,也會處於就緒狀態。bash

  第三是運行狀態。線程調度程序將處於就緒狀態的線程設置爲當前線程,此時線程就進入了運行狀態,開始運行run函數當中的代碼。微信

  第四是阻塞狀態。線程正在運行的時候,被暫停,一般是爲了等待某個時間的發生(好比說某項資源就緒)以後再繼續運行。sleep,suspend,wait等方法均可以致使線程阻塞。多線程

  第五是死亡狀態。若是一個線程的run方法執行結束或者調用stop方法後,該線程就會死亡。對於已經死亡的線程,沒法再使用start方法令其進入就緒。併發

2、start()方法

一、爲何須要start方法;它的做用是什麼?ide

start()方法來啓動線程,真正實現了多線程運行。 start方法的做用就是將線程由NEW狀態,變爲RUNABLE狀態。當線程建立成功時,線程處於NEW(新建)狀態,若是你不調用start( )方法,那麼線程永遠處於NEW狀態。調用start( )後,纔會變爲RUNABLE狀態,線程才能夠運行。函數

二、調用start()方法後,線程是否是立刻執行?學習

線程不是立刻執行的;準確來講,調用start( )方法後,線程的狀態是「READY(就緒)」狀態,而不是「RUNNING(運行中)」狀態(關於線程的狀態詳細。線程要等待CPU調度,不一樣的JVM有不一樣的調度算法,線程什麼時候被調度是未知的。所以,start()方法的被調用順序不能決定線程的執行順序

注意: 因爲在線程的生命週期中,線程的狀態由NEW ----> RUNABLE只會發生一次,所以,一個線程只能調用start()方法一次,屢次啓動一個線程是非法的。特別是當線程已經結束執行後,不能再從新啓動。

3、run( )方法

一、run方法又是一個什麼樣的方法?run方法與start方法有什麼關聯?

run()方法看成普通方法的方式調用 run( )實際上是一個普通方法,只不過當線程調用了start( )方法後,一旦線程被CPU調度,處於運行狀態,那麼線程纔會去調用這個run()方法;

二、run()方法的執行是否是須要線程調用start()方法

上面說了,run()方法是一個普通的對象方法,所以,不須要線程調用start()後才能夠調用的。能夠線程對象能夠隨時隨地調用run方法。

#Example1:

Thread t1 = new Thread(new MyTask(1));
  Thread t2 = new Thread(new MyTask(2));
     t1.run();
     t2.run();
複製代碼

上面的輸出結果是固定的:

count的值:1 count的值:2

再看另外一個實例:

Thread t1 = new Thread(new MyTask());
 Thread t2 = new Thread(new MyTask());
     t1.start();
     t2.start();
複製代碼

這個輸出結果不是固定的,由於線程的運行無法預測。運行結果可能不同。

MyTask 類:

//實現Runnable接口
class MyTask implements Runnable{

    int count;
    public MyTask(int count) {
        this.count=count;
    }
    @Override
    public void run() {
        System.out.println("count的值:"+count);
    }
}
複製代碼

#Example2:

一、用start方法啓動線程

public class Main {  
      
        public static void main(String[] args) {  
            Thread t1 = new Thread(new T1());  
            Thread t2 = new Thread(new T2());  
            t1.start();  
            t2.start();  
        }  
      
    }  
      
    class T1 implements Runnable {  
        public void run() {  
            try {  
                for(int i=0;i<10;i++){  
                    System.out.println(i);  
                    Thread.sleep(100);  //模擬耗時任務  
                }  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
      
    class T2 implements Runnable {  
        public void run() {  
            try {  
                for(int i=0;i>-10;i--){  
                    System.out.println(i);  
                    Thread.sleep(100);  //模擬耗時任務  
                }  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
複製代碼

結果:

這裏寫圖片描述
說明兩線程是併發執行的。

二、先用run方法啓動線程 將上面的start()改成run()

public class Main {  
      
        public static void main(String[] args) {  
            Thread t1 = new Thread(new T1());  
            Thread t2 = new Thread(new T2());  
            t1.run();  
            t2.run();  
        }  
      
    }  
複製代碼

這裏寫圖片描述
說明兩線程實際是順序執行的。

總結:

經過實例1和實例和咱們能夠知道start方法是用於啓動線程的,能夠實現併發,而run方法只是一個普通方法,是不能實現併發的,只是在併發執行的時候會調用。

說到這,不知道小夥伴們有沒有明白這兩個方法的區別,若是還有疑問,能夠留言交流。

4、start()方法和run()方法源碼解析(基於JDK1.7.0_40)

public synchronized void start() {  
        // 若是線程不是"就緒狀態",則拋出異常!  
        if (threadStatus != 0)  
            throw new IllegalThreadStateException();  
        // 將線程添加到ThreadGroup中  
        group.add(this);  
        boolean started = false;  
        try {  
            // 經過start0()啓動線程,新線程會調用run()方法  
            start0();  
            // 設置started標記=true  
            started = true;  
        } finally {  
            try {  
                if (!started) {  
                    group.threadStartFailed(this);  
                }  
            } catch (Throwable ignore) {  
            }  
        }  
    }  
複製代碼
public void run() {  
    if (target != null) {  
        target.run();  
    }  
} 
複製代碼

5、真正理解Thread類

Thread類的對象其實也是一個java對象,只不過每個Thread類的對象對應着一個線程。Thread類的對象就是提供給用戶用於操做線程、獲取線程的信息。真正的底層線程用戶是看不到的了。 所以,**當一個線程結束了,死掉了,對應的Thread的對象仍能調用,除了start( )方法外的全部方法(死亡的線程不能再次啓動),如run( )、getName( )、getPriority()**等等

//簡單起見,使用匿名內部類的方法來建立線程
    Thread thread = new Thread(){
        @Override
        public void run() {
            System.out.println("Thread對象的run方法被執行了");
        }
    };
    //線程啓動
    thread.start();
    
    //用循環去監聽線程thread是否還活着,只有當線程thread已經結束了,才跳出循環
    while(thread.isAlive()){}
    //線程thread結束了,但仍能調用thread對象的大部分方法
    System.out.println("線程"+thread.getName()+"的狀態:"+thread.getState()+"---優先級:"+thread.getPriority());
    //調用run方法
    thread.run();
    //當線程結束時,start方法不能調用,下面的方法將會拋出異常
    thread.start();
複製代碼
參考資料
  • http://www.cnblogs.com/jinggod/p/8485143.html
  • https://blog.csdn.net/u010568463/article/details/47911181
  • https://blog.csdn.net/xuxurui007/article/details/7685076

文章有不當之處,歡迎指正,你也能夠關注個人微信公衆號:好好學java,獲取優質學習資源。

相關文章
相關標籤/搜索