Java併發基礎01:揭祕傳統線程技術中建立線程的兩種方式

歡迎關注個人微信公衆號:程序員私房菜(id:eson_15)java

傳統的線程技術中有兩種建立線程的方式:一是繼承Thread類,並重寫run()方法;二是實現Runnable接口,覆蓋接口中的run()方法,並把Runnable接口的實現扔給Thread。這兩種方式大部分人可能都知道,可是爲何這樣玩就能夠呢?下面咱們來詳細分析一下這兩種方法的前因後果。程序員

1. 揭祕Thread中run()

上面咱們看到這兩種方式都跟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

2. 建立方式1:繼承Thread類

只要兩步便可建立並開啓一個線程:對象

  • 繼承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();//開啓線程
	}
}
複製代碼

3. 建立方式2:實現Runnable接口

只要兩步便可建立並開啓一個線程:

  • 實現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();
	}
}
複製代碼

4. 兩種方式同時使用

若是有個哥們比較給力,他兩種方式同時使用了,即:既實現了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,傳統的建立線程的兩種方式就總結這麼多~若有錯誤之處,歡迎指正~咱們一塊兒進步!

也歡迎你們關注個人微信公衆號:程序員私房菜。我會持續輸出更多文章。

公衆號
相關文章
相關標籤/搜索