歡迎關注個人微信公衆號:程序員私房菜(id:eson_15)java
傳統的線程技術中有兩種建立線程的方式:一是繼承Thread
類,並重寫run()
方法;二是實現Runnable
接口,覆蓋接口中的run()
方法,並把Runnable
接口的實現扔給Thread
。這兩種方式大部分人可能都知道,可是爲何這樣玩就能夠呢?下面咱們來詳細分析一下這兩種方法的前因後果。程序員
上面咱們看到這兩種方式都跟run()
方法有關,因此咱們來看一下Thread
的源碼中run()
方法到底都幹了什麼:微信
@Override
public void run() {
if (target != null) {
target.run();
}
}
複製代碼
咱們能夠看出,run()
方法中很簡單,只有一個if
語句,若是target不爲空就執行target的run()
方法,不然什麼也不幹,那麼這target究竟是何方神聖呢?咱們點擊進去能夠看到:ide
private Runnable target;
複製代碼
原來target就是Runnable接口,咱們再點進Runnable看看:spa
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
複製代碼
Runnable中就一個方法,也是run()
方法!好了,如今再回到Thread類的run()
方法中,若是target不爲空,即實現了Runnable接口,也即實現了Runnable中的run()
方法,那麼咱們就使用該接口中的run()
方法;若是target爲空,即沒有實現Runnable接口,那咱們什麼也不作,即線程建立後立馬就消失了。線程
因此到這裏,你們就明白了爲何建立線程有上面兩種方式了。第一種:你不是要先進行if
判斷麼?我如今不判斷了,我把你的if
幹掉,我在run()
方法中本身寫代碼,想幹啥就幹啥,即重寫Thread中的run()
方法,;第二種:你不是要先進行if
判斷麼?行,給你一個Runnable接口讓你判斷,但你仍是得調用我Runnable中的run()
方法啊,那我重寫我Runnable中的run()
方法不就好了! code
知道了前因後果後,下面就針對這兩種傳統的方式寫個實例。cdn
只要兩步便可建立並開啓一個線程:對象
Thread
類,並實現run()
方法;start()
方法開啓線程。因爲只要實現一個run()
方法便可,因此咱們能夠使用java中的匿名內部類來實現,以下:繼承
public class TraditionalThread {
public static void main(String[] args) {
/********** 第一種方法:繼承Thread類,覆寫run()方法 **************/
Thread thread1 = new Thread(){
@Override
public void run() {
try {
Thread.sleep(500);//讓線程休息500毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());//打印出當前線程名
}
};
thread1.start();//開啓線程
}
}
複製代碼
只要兩步便可建立並開啓一個線程:
Runnable
接口,並實現run()
方法;start()
方法開啓線程。因爲只要實現一個run()
方法便可,因此咱們也能夠使用java中的匿名內部類來實現,以下:
public class TraditionalThread {
public static void main(String[] args) {
/********** 第二種方法:實現Runnable接口,扔給Thread **************/
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
});
thread2.start();
}
}
複製代碼
若是有個哥們比較給力,他兩種方式同時使用了,即:既實現了Thread類中的run()
方法,又給Thread扔了一個實現了run()
方法的Runnable。以下所示:
public class TraditionalThread {
public static void main(String[] args) {
//這哥們的代碼寫的比較給力
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Runnable:" + Thread.currentThread().getName());
}
}){
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread:" + Thread.currentThread().getName());
}
}.start();
}
}
複製代碼
如今又會執行哪一個呢?咱們運行一下上面的程序就會發現,它會打印出Thread的信息,因此運行的是Thread的run()
方法,知道結論了,可是爲啥呢?
從面向對象的思想去考慮:上面一段代碼實際上是新new了一個對象(子對象)繼承了Thread對象(父對象),在子對象裏重寫了父類的run()
方法,父對象中扔了個Runnable進去,父對象中的run()
方法就是最初的帶有if
判斷的run()
方法。
好了,如今執行start()
後,確定先在子類中找run()
方法,找到了,父類的run()
方法天然就被幹掉了,因此會打印出Thread:,若是咱們如今假設子類中沒有重寫run()
方法,那麼必然要去父類找run()
方法,父類的run()
方法中就得判斷是否有Runnable傳進來,如今有一個,因此執行Runnable中的run()
方法,那麼就會打印Runnable:出來。
OK,傳統的建立線程的兩種方式就總結這麼多~若有錯誤之處,歡迎指正~咱們一塊兒進步!
也歡迎你們關注個人微信公衆號:程序員私房菜。我會持續輸出更多文章。