Java多線程之線程的啟動
一、前言
啟動線程的方法有如下兩種。
- 利用Thread 類的子類的實例啟動線程
- 利用Runnable 接口的實現類的實例啟動線程
最后再介紹下java.util.concurrent.ThreadFactory中的線程創建
下面分別做以介紹
二、利用Thread 類的子類啟動線程
這里來學習一下利用Thread 類的子類的實例來啟動線程的方法,即上一篇博文中使用的方法。我們構造一個PrintThread 類表示輸出1000次指定字符串的線程。輸出的字符串通過構造函數的參數傳入,並賦給message 字段。PrintThread 類被聲明為Thread 的子類。
如下表示輸出10 000 次指定字符串的線程的類PrintThread(PrintThread.java)
public class PrintThread extends Thread {
private String message;
public PrintThread(String message) {
this.message = message;
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(message);
}
}
}
Main 類是用於創建上面聲明的PrintThread 類的兩個實例並利用它們來啟動兩個線程的程序。
利用PrintThread 類啟動2 個線程(Main.java)
public class Main {
public static void main(String[] args) {
new PrintThread("Good!").start();
new PrintThread("Nice!").start();
}
}
main 方法創建了PrintThread 類的實例,並直接(不賦給某個變量)調用了該實例的start 方法。
start 方法會啟動新的線程,然后由啟動的新線程調用PrintThread 類的實例的run 方法。
最終結果就是由新啟動的線程執行1000次Good! 字符串輸出。
為了程序簡潔,上面的程序只用一條語句啟動了線程。但實際上,“創建PrintThread 的實例”和“啟動該實例對應的線程”是兩個完全不同的處理。也就是說,即便已經創建了實例,但是如果不調用start 方法,線程也不會被啟動。上面這條語句也可以像下面這樣寫成兩句。
另外,這里再提醒大家注意,“PrintThread 的實例”和“線程本身”不是同一個東西。即便創建了PrintThread 的實例,線程也並沒有啟動,而且就算線程終止了,PrintThread 的實例也不會消失。
主線程在Main 類的main 方法中啟動了兩個線程。隨后main 方法便會終止,主線程也會跟着終止。但整個程序並不會隨之終止,因為啟動的兩個線程在字符串輸出之前是不會終止的。直到所有的線程都終止后,程序才會終止。也就是說,當這兩個線程都終止后,程序才會終止。
關於程序的終止:
Java 程序的終止是指除守護線程(Daemon Thread)以外的線程全部終止。守護線程是執行后台作業的線程。我們可以通過setDaemon 方法把線程設置為守護線程。
創建Thread 類的子類、創建子類的實例、調用start 方法——這就是利用Thread 類的子類啟動線程的方法。
三、利用Runnable 接口啟動線程
這里來學習一下利用Runnable 接口的實現類的實例來啟動線程的方法。Runnable 接口包含在java.lang 包中,聲明如下。
public interface Runnable {
public abstract void run();
}
Runnable 接口的實現類必須要實現run 方法,否則要聲明為抽象方法。
現在我們來構造Printer類表示一個輸出10 000 次指定字符串的線程。輸出的字符串通過構造函數的參數傳入,並賦給message 字段。由於Printer 類實現(implements)了Runnable 接口,所以此時也就無需再將Printer 類聲明為Thread 類的子類。
輸出指定字符串的Printer 類(Printer.java)
public class Printer implements Runnable {
private String message;
public Printer(String message) {
this.message = message;
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(message);
}
}
}
Main 類是用於創建兩個Printer 類的實例,並利用它們來啟動兩個線程的程序。
利用Runnable 接口啟動兩個線程(Main.java)
public class Main {
public static void main(String[] args) {
new Thread(new Printer("Good")).start();
new Thread(new Printer("Nice")).start();
}
}
創建Thread 的實例時,構造函數的參數中會傳入Printer 類的實例,然后會調用start 方法,啟動線程。
start 方法會啟動新的線程,然后由啟動的新線程調用Printer 類的實例的run 方法。最終結果就是由新啟動的線程執行1000次Good! 字符串輸出。上面這條語句也可以像下面這樣寫成三句。
創建Runnable 接口的實現類,將實現類的實例作為參數傳給Thread 的構造函數,調用start 方法——這就是利用Runnable 接口啟動線程的方法。
不管是利用Thread 類的子類的方法(1),還是利用Runnable 接口的實現類的方法(2),啟動新線程的方法最終都是Thread 類的start 方法。
關於Thread 類和Runnable 方法
Thread 類本身還實現了Runnable 接口,並且持有run 方法,但Thread 類的run 方法主體是空的,不執行任何操作。Thread 類的run 方法通常都由子類的run 方法重寫(override)。
四、java.util.concurrent.ThreadFactory 中的線程創建
java.util.concurrent 包中包含一個將線程創建抽象化的ThreadFactory 接口。利用該接口,我們可以將以Runnable 作為傳入參數並通過new 創建Thread 實例的處理隱藏在ThreadFactory 內部。典型用法如下所示。默認的ThreadFactory 對象是通過Executors.defaultThreadFactory 方法獲取的。
此處運行的Printer類與上面的Printer 類相同
利用ThreadFactory 新啟動線程(Main.java)
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
public class Main2 {
public static void main(String[] args) {
ThreadFactory factory = Executors.defaultThreadFactory();
factory.newThread(new Printer("Good!")).start();
for (int i = 0; i < 1000; i++) {
System.out.println("Nice!");
}
}
}
參考:圖解Java多線程設計模式
