Java多線程學習筆記——建立新線程的4種方法

Java語言內置了多線程支持。當咱們啓動一個Java程序時,其實是啓動了一個JVM進程,而後JVM啓動主線程來執行main()方法。在main()方法中,咱們又能夠建立、啓動其餘線程。java

本質上來說,在Java中建立新線程十分簡單,只須要實例化一個Thread實例便可:segmentfault

Thread t = new Thread();
複製代碼

可是當咱們調用該實例的start()方法來啓動線程時,該線程其實什麼事情也沒幹就結束了,緣由是Thread類中覆寫Runnable接口的run()方法的方法體以下:多線程

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

這裏的target字段是Runnable接口的一個實例,而當Thread構造器沒傳入任何參數時,target爲null,線程天然也就不會執行任何操做。ide

在Java中,要建立能執行指定代碼的新線程,有如下幾種方法:學習

  1. Thread類派生一個自定義類,而後覆寫run()方法。
  2. 建立Thread實例時,傳入一個Runnadble接口的實例,一樣要實現接口定義的run()抽象方法。
  3. 實現Callable接口,重寫call()方法,幷包裝成FutureTask類對象,再做爲參數傳入Thread的構造器。
  4. 使用線程池建立新線程。

注意:網站

  1. 方法1和方法2由於都是經過覆寫run()方法來建立新線程,而run()方法並沒有返回值,因此這兩種方法也沒法獲取返回值。
  2. 在實際項目工程中,最好不要在應用中自行顯式建立線程(方法一、二、3),線程資源要經過線程池提供。使用線程池的好處是減小在建立和銷燬線程上所消耗的時間以及系統資源的開銷,解決資源不足的問題。若是不使用線程池,有可能形成系統建立大量同類線程而致使消耗完內存或者「過分切換」的問題。

1. 繼承Thread類,覆寫run()方法

public class ExecuteThread {
  public static void main(String[] args) {
    Thread t = new MyThread();
    t.start();	// 啓動新線程
  }
}

// 繼承自Thread的自定義類
class MyThread extends Thread {
  @Override
  public void run() {
    System.out.println("start new thread!");
  }
}
複製代碼

若是該類型的線程只須要使用一次,也能夠用匿名內部類的方式讓代碼更加簡單:編碼

public class ExecuteThread {
  public static void main(String[] args) {
    Thread t = new Thread() {
      // 內部匿名類對run()方法的覆寫
    	@Override
  		public void run() {
    		System.out.println("start new thread!");
  		}
    };
    t.start();	// 啓動新線程
  }
}
複製代碼
  • 優勢:編碼簡單,容易理解。
  • 缺點:不能繼承其餘類,功能單一。

2. 實現Runnadble接口,實現run()抽象方法

public class ExecuteThread {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable());
        t.start(); // 啓動新線程
    }
}

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("start new thread!");
    }
}
複製代碼

或者用Java8引入的Lambda語法進一步簡寫爲:spa

public class ExecuteThread {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println("start new thread!");
        });
        t.start(); // 啓動新線程
    }
}
複製代碼
  • 優勢:
    1. 傳入的實例實現的是Runnadble接口,因此能夠繼承其餘的類,避免了單繼承的侷限性。
    2. 適合多個相同程序代碼的線程共享一個資源(同一個線程任務對象根據傳入構造器的不一樣Runnadble接口實例,能夠被包裝成多個線程對象),實現解耦操做,代碼和線程獨立。
  • 缺點:實現相對複雜一些。

3. 實現Callable接口,重寫call()方法,幷包裝成FutureTask對象傳入Thread構造器

public class ExecuteThread {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<String> callableInstance = new MyCallable<>("return value");
        // 使用FutureTask類包裝Callable接口的實例,該對象封裝了Callable接口實現實例的call()方法的返回值。
        FutureTask<String> task = new FutureTask<>(callableInstance);
        Thread t = new Thread(task);
        t.start();  // 啓動新線程
        // 調用FutureTask實例的get()方法獲取新線程執行結束返回值。
        System.out.println(task.get());
    }
}

class MyCallable<V> implements Callable<V> {
    private V toReturn;

    public MyCallable (V val) {
        toReturn = val;
    }

    @Override
    public V call() throws Exception{
        System.out.println("start new thread!");
        return toReturn;
    }
}
複製代碼
  • 優勢:相對於方法2(傳入Runnadble實例),能夠獲取返回值。
  • 缺點:實現比較複雜。

4. 使用線程池建立新線程

線程池的使用避免了由於頻繁建立、銷燬線程帶來的大量系統開銷,實現了資源的複用。具體的使用方法看這篇筆記:Java多線程學習筆記——如何使用線程池線程

參考連接

相關文章
相關標籤/搜索