線程的啓動流程剖析及使用的設計模式

我們都知道,Java中構造線程的方式有兩種,第一種是繼承Thread類,然後覆寫run方法;第二種是實現Runnable接口,然後實現run方法。但是最終啓動的時候都是通過Thread對象的start方法啓動的。那麼既然邏輯寫在了run方法中,啓動的時候爲什麼調用start方法呢?

1.JDK Thread類的start方法源碼解析

public synchronized void start() {
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
    group.add(this);
    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
        }
    }
}

通過查看源碼可以看到,在start方法中調用了start0這個方法,這個方法的實現如下:

private native void start0();

該方法是native方法,表示由JDK通過JNI來調用的。但是run方法何時調用的呢?
通過查看官方文檔,可以看到說明,start0的底層實現中是調用了run方法的,只不過是通過C++實現的,感興趣的可以翻一下對應的代碼。

通過上述start方法的源碼,可以總結出以下幾個點:

  • (1) 當線程被構造完成之後,有個狀態變量會記錄線程的狀態,就是上述的threadStatus,初始值在類中聲明的爲0;
  • (2) 調用start方法啓動線程之後,threadStatus的值會被改變。如果再次重複調用start方法啓動同一個線程時,會拋出IllegalThreadStateException異常;
  • (3) thread線程啓動之後,會被加入到一個線程組ThreadGroup中;
  • (4) 線程如果啓動失敗了,線程組會對線程做失敗處理,將其從線程組中移除;
  • (5) 如果一個線程生命週期結束了,再次調用start方法試圖啓動線程的時候,會提示IllegalThreadStateException異常,也就是處於TERMINATED狀態的線程沒辦法重新回到RUNNABLE或者RUNNING狀態;

示例驗證1:驗證線程是否可以啓動多次

Thread thread = new Thread(){
    @Override
    public void run() {
        try {
            TimeUnit.MILLISECONDS.sleep(5);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
};
thread.start();
thread.start();

運行上述代碼,會出現以下異常信息:

Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:705)
at com.wb.spring.test.TestThread.main(TestThread.java:40)

示例驗證2:線程生命週期結束,再次調用start方法啓動

Thread thread = new Thread(){
    @Override
    public void run() {
        try {
            TimeUnit.MILLISECONDS.sleep(1);
            System.out.println("線程運行完畢!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
};
// 第一次啓動
thread.start();
try {
    TimeUnit.MILLISECONDS.sleep(3);
} catch (Exception e) {
    e.printStackTrace();
}
// 第二次啓動
thread.start();

運行以上示例代碼,將會得到如下的結果:

線程運行完畢!
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:705)
at com.wb.spring.test.TestThread.main(TestThread.java:48)

從結果中可以看出,線程運行完成之後,生命週期就結束了,不能再次啓動。

從上述示例中可以看到,都跑出了IllegalThreadStateException異常,但是兩次出現該異常的情況卻不一樣。第一種線程還活着,生命週期未結束,只是不允許多次重複啓動;而第二次是因爲線程的生命週期已經結束,試圖啓動已經結束的線程,就會拋出該異常。

2.Thread類中的設計模式-模板設計模式

簡介:模板設計模式指的是在繼承結構中,父類中來確定算法結構,而具體的算法實現細節交給子類來完成。比如Thread類中run方法的源碼如下,如果使用了Thread類的方式構造線程,其實run方法相當於一個空的實現,但是調用start方法的時候,卻能夠正確執行子類run方法的實現細節。

@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

這種設計模式有利於父類確定程序的結構。
模板設計模式示例:

public class TemplateTest {
    public final void say(String msg) {
        System.out.println("000000");
        sayHello(msg);
        System.out.println("000000");
    }

    protected void sayHello(String msg) {
        
    }

    public static void main(String[] args) {
        TemplateTest test1 = new TemplateTest() {
            @Override
            protected void sayHello(String msg) {
                System.out.println("***" + msg + "***");
            }
        };
        test1.say("wb");
        TemplateTest test2 = new TemplateTest() {
            @Override
            protected void sayHello(String msg) {
                System.out.println("&&&" + msg + "&&&");
            }
        };
        test2.say("bw");
    }
}

輸入結果如下:

000000
***wb***
000000
000000
&&&bw&&&
000000

可以看到,需要輸入什麼符號,可以自由地由子類來控制!這就是模板設計模式帶來的好處!

以上就是Thread類啓動線程的過程及使用的設計模式詳解。歡迎評論轉發。
另提供一些免費的IT視頻資料,架構師,高可用,高併發等教程!如需要請查看https://www.592xuexi.com

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章