文章目錄
- 概述
- 繼承Thread類建立線程
- 實作Runnable接口建立線程
- 使用Callable和Future建立線程
- 三種建立線程方法對比
概述
Java使用Thread類代表線程,所有的線程對象都必須是Thread類或其子類的執行個體。Java可以用三種方式來建立線程,如下所示:
- 繼承Thread類建立線程
- 實作Runnable接口建立線程
- 使用Callable和Future建立線程
下面讓我們分别來看看這三種建立線程的方法。
繼承Thread類建立線程
通過繼承Thread類來建立并啟動多線程的一般步驟如下
- d定義Thread類的子類,并重寫該類的run()方法,該方法的方法體就是線程需要完成的任務,run()方法也稱為線程執行體。
- 建立Thread子類的執行個體,也就是建立了線程對象
- 啟動線程,即調用線程的start()方法
代碼執行個體
public class MyThread extends Thread{//繼承Thread類
public void run(){
//重寫run方法
}
}
public class Main {
public static void main(String[] args){
new MyThread().start();//建立并啟動線程
}
}
實作Runnable接口建立線程
通過實作Runnable接口建立并啟動線程一般步驟如下:
- 定義Runnable接口的實作類,一樣要重寫run()方法,這個run()方法和Thread中的run()方法一樣是線程的執行體
- 建立Runnable實作類的執行個體,并用這個執行個體作為Thread的target來建立Thread對象,這個Thread對象才是真正的線程對象
- 第三部依然是通過調用線程對象的start()方法來啟動線程
代碼執行個體:
public class MyThread2 implements Runnable {//實作Runnable接口
public void run(){
//重寫run方法
}
}
public class Main {
public static void main(String[] args){
//建立并啟動線程
MyThread2 myThread=new MyThread2();
Thread thread=new Thread(myThread);
thread().start();
//或者 new Thread(new MyThread2()).start();
}
}
使用Callable和Future建立線程
和Runnable接口不一樣,Callable接口提供了一個call()方法作為線程執行體,call()方法比run()方法功能要強大。
- call()方法可以有傳回值
- call()方法可以聲明抛出異常
Java5提供了Future接口來代表Callable接口裡call()方法的傳回值,并且為Future接口提供了一個實作類FutureTask,這個實作類既實作了Future接口,還實作了Runnable接口,是以可以作為Thread類的target。在Future接口裡定義了幾個公共方法來控制它關聯的Callable任務。
boolean cancel(boolean mayInterruptIfRunning):視圖取消該Future裡面關聯的Callable任務
V get():傳回Callable裡call()方法的傳回值,調用這個方法會導緻程式阻塞,必須等到子線程結束後才會得到傳回值
V get(long timeout,TimeUnit unit):傳回Callable裡call()方法的傳回值,最多阻塞timeout時間,經過指定時間沒有傳回抛出TimeoutException
boolean isDone():若Callable任務完成,傳回True
boolean isCancelled():如果在Callable任務正常完成前被取消,傳回True
介紹了相關的概念之後,建立并啟動有傳回值的線程的步驟如下:
- 建立Callable接口的實作類,并實作call()方法,然後建立該實作類的執行個體(從java8開始可以直接使用Lambda表達式建立Callable對象)。
- 使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了Callable對象的call()方法的傳回值
- 使用FutureTask對象作為Thread對象的target建立并啟動線程(因為FutureTask實作了Runnable接口)
- 調用FutureTask對象的get()方法來獲得子線程執行結束後的傳回值
代碼執行個體:
public class Main {
public static void main(String[] args){
MyThread3 th=new MyThread3();
//使用Lambda表達式建立Callable對象
//使用FutureTask類來包裝Callable對象
FutureTask<Integer> future=new FutureTask<Integer>(
(Callable<Integer>)()->{
return 5;
}
);
new Thread(task,"有傳回值的線程").start();//實質上還是以Callable對象來建立并啟動線程
try{
System.out.println("子線程的傳回值:"+future.get());//get()方法會阻塞,直到子線程執行結束才傳回
}catch(Exception e){
ex.printStackTrace();
}
}
三種建立線程方法對比
- 線程隻是實作Runnable或實作Callable接口,還可以繼承其他類。
- 這種方式下,多個線程可以共享一個target對象,非常适合多線程處理同一份資源的情形。
- 但是程式設計稍微複雜,如果需要通路目前線程,必須調用Thread.currentThread()方法。
- 繼承Thread類的線程類不能再繼承其他父類(Java單繼承決定)。