多線程
多線程簡(jiǎn)介
什么是線程?
線程(thread)是一個(gè)程序內(nèi)部的一條執(zhí)行路徑。
我們之前啟動(dòng)程序執(zhí)行后,main方法的執(zhí)行其實(shí)就是一條單獨(dú)的執(zhí)行路徑。
public static void main(String[] args) { // 代碼... for (int i = 0; i < 10; i++) { System.out.println(i); } // 代碼... }
程序中如果只有一條執(zhí)行路徑,那么這個(gè)程序就是單線程的程序。
什么是多線程?
多線程是指從軟硬件上實(shí)現(xiàn)多條執(zhí)行流程的技術(shù)。
多線程創(chuàng)建
方式一: 繼承Thread類
Thread類:
Java是通過(guò)java.lang.Thread 類來(lái)代表線程的。
按照面向?qū)ο蟮乃枷?,Thread類應(yīng)該提供了實(shí)現(xiàn)多線程的方式。
Thread類創(chuàng)建線程方式:
- 定義一個(gè)子類MyThread繼承線程類java.lang.Thread
- 子類MyThread中重寫run()方法
- 創(chuàng)建MyThread類的實(shí)例對(duì)象
- 調(diào)用子線程對(duì)象的start()方法啟動(dòng)線程(啟動(dòng)后還是執(zhí)行run方法的)
Thread類創(chuàng)建多線程示例代碼如下:
public class ThreadDemo {
public static void main(String[] args) {
// 3. 創(chuàng)建MyThread類的實(shí)例對(duì)象
Thread t = new MyThread();
// 4. 調(diào)用子線程對(duì)象的start方法啟動(dòng)線程(啟動(dòng)后還是執(zhí)行的run方法)
t.start();
// 主線程中執(zhí)行的操作
for (int i = 0; i < 5; i++) {
System.out.println("主線程執(zhí)行輸出: " + i);
}
}
}
/**
1. 定義一個(gè)線程類繼承自Thread
*/
class MyThread extends Thread {
/**
2. 重寫run方法, run方法中定義創(chuàng)建出來(lái)的線程要做什么
*/
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子線程執(zhí)行輸出: " + i);
}
}
}
Thread類創(chuàng)建多線程的有缺點(diǎn):
優(yōu)點(diǎn):編碼簡(jiǎn)單
缺點(diǎn):線程類已經(jīng)繼承Thread,無(wú)法繼承其他類,不利于擴(kuò)展; 如果線程有執(zhí)行結(jié)果是不可以直接返回的, 意思是重寫的run方法如果是有返回值是沒(méi)辦法獲取到的, 該方式只適合執(zhí)行功能。
上面提到, 調(diào)用start方法啟動(dòng)后還是執(zhí)行的run方法, 那么為什么不直接執(zhí)行run方法呢?
直接調(diào)用run方法會(huì)當(dāng)成普通方法執(zhí)行,此時(shí)相當(dāng)于還是單線程執(zhí)行。
只有調(diào)用start方法才是啟動(dòng)一個(gè)新的線程執(zhí)行。
注意: 不要把主線程執(zhí)行任務(wù)的代碼放在子線程之前, 因?yàn)檫@樣做主線程一直是先跑完的才開(kāi)啟子線程,相當(dāng)于是一個(gè)單線程的效果了。
方式二: 實(shí)現(xiàn)Runnable接口
通過(guò)實(shí)現(xiàn)Runnable接口創(chuàng)建多線程方式如下:
- 定義一個(gè)線程任務(wù)類MyRunnable實(shí)現(xiàn)Runnable接口
- 任務(wù)類MyRunnable中重寫run()方法
- 創(chuàng)建MyRunnable任務(wù)類對(duì)象
- 把MyRunnable任務(wù)對(duì)象交給Thread處理。
- 調(diào)用線程對(duì)象的start()方法啟動(dòng)線
實(shí)現(xiàn)Runnable接口創(chuàng)建多線程示例代碼:
public class ThreadDemo2 {
public static void main(String[] args) {
// 3. 創(chuàng)建MyRunnable 任務(wù)對(duì)象
Runnable target = new MyRunnable();
// 4. 把任務(wù)對(duì)象交給線程對(duì)象(Thread)處理
Thread t = new Thread(target);
// 5. 啟動(dòng)線程
t.start();
// 主線程要執(zhí)行的操作
for (int i = 0; i < 5; i++) {
System.out.println("主線程執(zhí)行輸出: " + i);
}
}
}
/**
1. 定義一個(gè)線程任務(wù)類, 實(shí)現(xiàn)Runnable接口
*/
class MyRunnable implements Runnable {
/**
2. 任務(wù)類MyRunnable中重寫run方法
*/
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子線程執(zhí)行輸出: " + i);
}
}
}
實(shí)現(xiàn)Runnable接口創(chuàng)建多線程, 匿名內(nèi)部類的方式簡(jiǎn)化方式一:
public static void main(String[] args) {
// 1. 創(chuàng)建任務(wù)對(duì)象(匿名內(nèi)部類的方式)
Runnable target = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子線程執(zhí)行輸出: " + i);
}
}
};
// 2. 將任務(wù)對(duì)象交給線程對(duì)象(Thread)處理
Thread t = new Thread(target);
// 3. 啟動(dòng)線程
t.start();
// 主線程執(zhí)行的操作
for (int i = 0; i < 5; i++) {
System.out.println("主線程執(zhí)行輸出: " + i);
}
}
實(shí)現(xiàn)Runnable接口創(chuàng)建多線程, 匿名內(nèi)部類的方式簡(jiǎn)化方式二:
public static void main(String[] args) {
// 1. 創(chuàng)建任務(wù)對(duì)象(匿名內(nèi)部類的方式)
// 2. 將任務(wù)對(duì)象交給線程對(duì)象(Thread)處理
// 3. 啟動(dòng)線程
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子線程執(zhí)行輸出: " + i);
}
}
}).start();
// 主線程執(zhí)行的操作
for (int i = 0; i < 5; i++) {
System.out.println("主線程執(zhí)行輸出: " + i);
}
}
實(shí)現(xiàn)Runnable接口創(chuàng)建多線程, 匿名內(nèi)部類結(jié)合Lambda表達(dá)式的方式簡(jiǎn)化方式三:
public static void main(String[] args) {
// 1. 創(chuàng)建任務(wù)對(duì)象(匿名內(nèi)部類的方式)
// 2. 將任務(wù)對(duì)象交給線程對(duì)象(Thread)處理
// 3. 啟動(dòng)線程
new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("子線程執(zhí)行輸出: " + i);
}
}).start();
// 主線程執(zhí)行的操作
for (int i = 0; i < 5; i++) {
System.out.println("主線程執(zhí)行輸出: " + i);
}
}
實(shí)現(xiàn)Runnable接口創(chuàng)建多線程的優(yōu)缺點(diǎn):
優(yōu)點(diǎn):線程任務(wù)類只是實(shí)現(xiàn)接口,可以繼續(xù)繼承類和實(shí)現(xiàn)接口,擴(kuò)展性強(qiáng)。
缺點(diǎn):編程多一層對(duì)象包裝,如果線程有執(zhí)行結(jié)果是拿不到返回結(jié)果的。
方式三: 實(shí)現(xiàn)Callable接口
前兩種線程創(chuàng)建方式都存在一個(gè)問(wèn)題:
他們重寫的run方法均不能直接返回結(jié)果。
不適合需要返回線程執(zhí)行結(jié)果的業(yè)務(wù)場(chǎng)景。
那么如何解決這個(gè)問(wèn)題呢?
JDK 5.0提供了Callable和FutureTask結(jié)合使用來(lái)實(shí)現(xiàn)多線程。
這種實(shí)現(xiàn)多線程方式的優(yōu)點(diǎn)是:可以得到線程執(zhí)行的結(jié)果。
多線程的實(shí)現(xiàn)方案三:利用Callable、FutureTask接口實(shí)現(xiàn), 實(shí)現(xiàn)步驟如下:
① 得到任務(wù)對(duì)象
- 定義類實(shí)現(xiàn)Callable接口,重寫call方法,call方法中封裝我們要做的事情, 并且call方法是可以返回結(jié)果的。
- 把Callable任務(wù)對(duì)象交給FutureTask封裝成線程任務(wù)對(duì)象。
② 把線程任務(wù)對(duì)象交給線程對(duì)象(Thread)處理。
③ 調(diào)用Thread的start方法啟動(dòng)線程,執(zhí)行任務(wù)
④ 線程執(zhí)行完畢后、通過(guò)FutureTask的get方法去獲取任務(wù)執(zhí)行的結(jié)果。
使用FutureTask包裝的作用:
作用一: Thread線程對(duì)象構(gòu)造器只接受Runnable類型的對(duì)象, FutureTask是實(shí)現(xiàn)了Runnable接口的, 可以交給Thread處理
作用二: 可以在線程執(zhí)行完畢之后通過(guò)調(diào)用其get方法得到線程執(zhí)行完成的結(jié)果
FutureTask的API
方法名稱 | 說(shuō)明 |
---|---|
FutureTask<>(Callable call) | 把Callable對(duì)象封裝成FutureTask對(duì)象。 |
get() throws Exception | 獲取線程執(zhí)行call方法返回的結(jié)果。 |
演示代碼
public class ThreadDemo4 {
public static void main(String[] args) {
// 3. 創(chuàng)建任務(wù)對(duì)象
Callable<String> callable = new MyCallable(5);
// 4. 將任務(wù)對(duì)象包裝成線程任務(wù)對(duì)象
FutureTask<String> ft = new FutureTask<>(callable);
// 5. 將線程任務(wù)對(duì)象交給線程對(duì)象(Thread)處理
Thread t = new Thread(ft);
// 6. 啟動(dòng)線程
t.start();
// 獲取線程執(zhí)行的結(jié)果, 會(huì)返回正常的結(jié)果和異常的結(jié)果
try {
String res = ft.get();
System.out.println(res);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
1. 定義類實(shí)現(xiàn)Callable接口
泛型自定義返回值類型
*/
class MyCallable implements Callable<String> {
private int n;
public MyCallable(int n) {
this.n = n;
}
/**
2. 重新實(shí)現(xiàn)類call方法(任務(wù)方法)
*/
@Override
public String call() throws Exception {
int sum = 0;
for (int i = 1; i <= n ; i++) {
sum += i;
}
return "子線程方法執(zhí)行的結(jié)果是" + sum;
}
}
方式三的優(yōu)缺點(diǎn):
優(yōu)點(diǎn):線程任務(wù)類只是實(shí)現(xiàn)接口,可以繼續(xù)繼承類和實(shí)現(xiàn)接口,擴(kuò)展性強(qiáng)。
可以在線程執(zhí)行完畢后去獲取線程執(zhí)行的結(jié)果。
缺點(diǎn):編碼會(huì)顯得復(fù)雜一點(diǎn)。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-828301.html
總結(jié): 三種方式對(duì)比如下文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-828301.html
方式 | 優(yōu)點(diǎn) | 缺點(diǎn) |
---|---|---|
繼承Thread類 | 編程比較簡(jiǎn)單,可以直接使用Thread類中的方法 | 擴(kuò)展性較差,不能再繼承其他的類,不能返回線程執(zhí)行的結(jié)果 |
實(shí)現(xiàn)Runnable接口 | 擴(kuò)展性強(qiáng),實(shí)現(xiàn)該接口的同時(shí)還可以繼承其他的類。 | 編程相對(duì)復(fù)雜,不能返回線程執(zhí)行的結(jié)果 |
實(shí)現(xiàn)Callable接口 | 擴(kuò)展性強(qiáng),實(shí)現(xiàn)該接口的同時(shí)還可以繼承其他的類??梢缘玫骄€程執(zhí)行的結(jié)果 | 編程相對(duì)復(fù)雜 |
到了這里,關(guān)于Java多線程 - 創(chuàng)建的三種方式介紹的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!