1、實現多線程一共有兩種方法(官方文檔這麼說的)
(1)聲明類是Thread的子類(即繼承Thread),不推薦使用這種方法
缺點:從代碼架構角度去考慮,run()方法就是我們具體執行的內容,run()方法的執行應該與我們線程(Thread)的創建、執行是解耦的,不應該把二者混爲一談。
繼承Thread實現線程,這種時候我們每次新建一個任務,只能去創建一個新的線程,而線程的創建、執行、銷燬是十分消耗資源的,但是通過實現Runnable接口的方法,我們就可以利用線程池之類的工具,可以大大減小資源的消耗。
Java是不允許雙繼承的,一旦繼承Thread,就無法繼承其他的類,是的類的擴展性降低。
public class ThreadStyle extends Thread {
@Override
public void run() {
System.out.println("通過集成Thread類實現多線程");
}
public static void main(String[] args) {
new ThreadStyle().start();
}
}
(2)聲明一個實現Runnable接口的類,然後實現run()方法(即實現Runnable接口),這個方法相對更好。
public class RunnableStyle implements Runnable {
@Override
public void run() {
System.out.println("通過實現Runnable接口實現多線程");
}
public static void main(String[] args) {
Thread t1 = new Thread(new RunnableStyle());
t1.start();
}
}
以上兩種方法都是執行run(),只不過run()的來源不同,以下是Thread類原本run()方法的代碼
@Override
public void run() {
if (target != null) {
target.run();
}
}
上面代碼中的target就是Runnbale(第二種方法在創建線程的時候,傳入的)
用第一種方法實現線程的時候,因爲要重寫run()方法,所以線程最後執行的方法run()是我們重寫的run()方法。
用第二種方法實現線程的時候,因爲要執行所傳入的Runnable的run()方法,所以線程最後執行的run()方法是我們實現的run()方法。
3、同時使用兩種實現線程的方式會怎麼樣
4、網上錯誤觀點
(1)線程池創建線程也算是一種新建線程的方式
錯誤原因:線程池創建線程的原理也是使用的new Thread()的方式
(2)通過Callable和FutureTask創建線程,也算是一種新建線程的方式
錯誤原因:
(3)定時器
(4)匿名內部類
(5)Lambda表達式
網上創建線程的方式多種多樣,但是其本質只有以上兩種創建線程的方式。
5、實現多線程的常見面試問題
問題1:有多少種實現線程的方法
從不同的角度看,會有不同的答案。
1)典型回答:兩種;一種是繼承Thread類;另一種是實現Runnable接口(這種方式實現更好),但是看原理,這兩種方式的本質是一樣的,因爲run()方法的代碼如下,(代碼省略),這f兩種方法都是基於Thread的run()方法實現的,只不過一種是重寫run()方法,一種是通過執行run()方法中的target.run()去實現的。
總結回答:從本質上講只有一種,都需要新建Thread類,但是通常我們區分爲兩種方法,一種是繼承Thread類,一種是實現Runnable接口,另外還有一些外在形式(代碼實現上)上去實現多線程,如線程池、計時器等。
問題2:實現Runnable接口和繼承Thread類那種方式更好
1)從代碼架構角度:這裏其實是分有兩件事情的,一就是具體的任務,也就是run()方法要執行的內容;二就是跟線程的生命週期相關的,如創建、運行、銷燬線程,而這件事情應該是Thread類應該去做的事情,這兩件事情的目的不也一樣,從代碼架構角度看,應該解耦,所以事先Runnable接口的方法更好點。
2)新建線程的損耗:如果我們使用繼承Thread這種方式去實現線程,那麼每次我們想要新建啓動一個任務,都需要new 一個Thread類,這樣損耗比較大,每一次都需要經過創建和銷燬線程的步驟,但是如果,我們通過實現Runnable接口這種方式去實現多線程的話,就可以通過線程池中的線程去執行任務,同一個線程可以被重複使用,這樣就減少了在線程在創建和銷燬上的消耗。
3)Java不支持雙繼承:一旦繼承了Thread類,就無法再去繼承其他的類,會降低程序的可擴展性,比如我們要繼承一個基類。