一文帶你搞懂Java中線程的創(chuàng)建方式
一. 繼承Thread
可以通過創(chuàng)建Thread的子類并在子類中重寫run() 方法完成線程創(chuàng)建。示例如下所示。
public class ThreadTest {
@Test
public void 繼承Thread() throws Exception {
// 創(chuàng)建線程對象
MyThread myThread = new MyThread();
// 啟動線程
myThread.start();
// 睡1秒,等待子線程執(zhí)行完任務(wù)
Thread.sleep(1000);
}
private static class MyThread extends Thread {
@Override
public void run() {
System.out.println("線程執(zhí)行了");
}
}
}運行測試程序,打印如下。

其實可以只繼承Thread,而不重寫run() 方法,此時是不會報錯的,只不過調(diào)用start() 方法后線程不會執(zhí)行任何邏輯。示例如下。
public class ThreadTest {
@Test
public void 繼承Thread時可以不重寫run方法() {
// 創(chuàng)建沒有重寫run()方法的線程對象
MyThreadNotOverrideRun myThread = new MyThreadNotOverrideRun();
// 啟動線程,不報錯,執(zhí)行的是Thread的run()方法,無任何邏輯
myThread.start();
}
private static class MyThreadNotOverrideRun extends Thread {}
}二. 創(chuàng)建Runnable對象
可以通過創(chuàng)建Runnable接口的實現(xiàn)類,然后將Runnable對象作為Thread對象的執(zhí)行任務(wù),來創(chuàng)建線程。示例如下。
public class ThreadTest {
@Test
public void 基于Runnable() throws Exception {
// 創(chuàng)建Runnable任務(wù)對象
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("任務(wù)執(zhí)行");
}
};
// 創(chuàng)建Thread對象時將Runnable任務(wù)對象通過構(gòu)造函數(shù)傳入
Thread thread = new Thread(runnable);
// 啟動線程
thread.start();
// 睡1秒,等待子線程執(zhí)行完任務(wù)
Thread.sleep(1000);
}
}運行測試程序,執(zhí)行結(jié)果如下所示。

三. 創(chuàng)建Callable對象
Callable接口也是可以作為任務(wù)被線程執(zhí)行,其與Runnable接口的區(qū)別在于Callable任務(wù)可以有返回值,而Runnable任務(wù)沒有返回值。
由于Thread對象只能執(zhí)行Runnable任務(wù),因此無法直接讓Thread執(zhí)行Callable任務(wù),但是可以先將Callable封裝成FutureTask,而FutureTask是實現(xiàn)了Runnable接口的,所以Thread對象可以執(zhí)行FutureTask任務(wù)。示例如下。
public class ThreadTest {
@Test
public void 基于Callable() throws Exception {
// 創(chuàng)建Callable任務(wù)對象
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
return "任務(wù)執(zhí)行結(jié)果";
}
};
// 將Callable封裝成FutureTask
FutureTask<String> futureTask = new FutureTask<>(callable);
// 創(chuàng)建Thread對象時將FutureTask通過構(gòu)造函數(shù)傳入
Thread thread = new Thread(futureTask);
// 啟動線程
thread.start();
// 通過FutureTask拿到執(zhí)行結(jié)果
System.out.println(futureTask.get());
}
}運行測試程序,結(jié)果如下。

四. 基于Runnable創(chuàng)建FutureTask
在第三小節(jié)中是基于Callable來創(chuàng)建的FutureTask,本小節(jié)將基于Runnable來創(chuàng)建FutureTask。在此之前,先看一下FutureTask的類圖,如下所示。

所以FutureTask即能夠作為Runnable被執(zhí)行,也能夠作為Future獲取異步執(zhí)行的結(jié)果。FutureTask有兩個構(gòu)造函數(shù),簽名如下。
// 基于Callable創(chuàng)建FutureTask public FutureTask(Callable<V> callable) // 基于Runnable創(chuàng)建FutureTask public FutureTask(Runnable runnable, V result)
下面重點看一下如何基于Runnable創(chuàng)建FutureTask,源碼如下所示。
public FutureTask(Runnable runnable, V result) {
// 使用Executors工具類將Runnable封裝成Callable
this.callable = Executors.callable(runnable, result);
this.state = NEW;
}繼續(xù)看Executors#callable(java.lang.Runnable, T) 方法,如下所示。
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null) {
throw new NullPointerException();
}
// 將Runnable封裝成RunnableAdapter
return new RunnableAdapter<T>(task, result);
}那么Executors#callable(java.lang.Runnable, T) 方法中就是將Runnable封裝成了RunnableAdapter,最后再看一下RunnableAdapter的實現(xiàn)。
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
// 執(zhí)行Runnable的邏輯
task.run();
// 執(zhí)行完畢后,result作為結(jié)果返回
return result;
}
}所以這里可以知道,基于Runnable創(chuàng)建FutureTask,其本質(zhì)是將Runnable先封裝為Callable,然后再將Callable封裝成FutureTask。還有一點需要注意,在基于Runnable創(chuàng)建FutureTask時,除了傳入Runnable,還可以傳入一個作為返回結(jié)果的對象,Runnable執(zhí)行完畢后,會將這個對象返回,這個對象也可以傳一個null,表示不需要返回值。
基于Runnable創(chuàng)建FutureTask的一個示例如下。
public class ThreadTest {
@Test
public void 基于Runnable來構(gòu)建FutureTask() throws Exception {
// 創(chuàng)建結(jié)果對象
MyResult myResult = new MyResult();
// 創(chuàng)建Runnable任務(wù)對象
Runnable runnable = new Runnable() {
@Override
public void run() {
myResult.setResult("任務(wù)執(zhí)行");
}
};
// 將Runnable封裝成FutureTask
// Runnable執(zhí)行后,會改變MyResult對象
FutureTask<MyResult> futureTask = new FutureTask<>(runnable, myResult);
// 創(chuàng)建Thread對象時將FutureTask通過構(gòu)造函數(shù)傳入
Thread thread = new Thread(futureTask);
// 啟動線程
thread.start();
// 通過FutureTask拿到執(zhí)行結(jié)果
System.out.println(futureTask.get().getResult());
}
private static class MyResult {
String result;
public MyResult() {}
public MyResult(String result) {
this.result = result;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
}
}運行測試程序,結(jié)果如下所示。

總結(jié)
線程的創(chuàng)建,總的概括有三種方式。
- 繼承
Thread并重寫run()方法; - 創(chuàng)建
Thread并執(zhí)行Runnable任務(wù); - 創(chuàng)建
Thread并執(zhí)行Callable任務(wù)。
以上就是一文帶你搞懂Java中線程的創(chuàng)建方式的詳細內(nèi)容,更多關(guān)于Java線程創(chuàng)建方式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決JSONObject.toJSONString()輸出null的問題
這篇文章主要介紹了解決JSONObject.toJSONString()輸出null的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02
RocketMQ?NameServer架構(gòu)設(shè)計啟動流程
這篇文章主要為大家介紹了RocketMQ?NameServer架構(gòu)設(shè)計啟動流程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02

