三種新增執行緒的方式及其區別
我們常見的新增執行緒的方法通常是兩種: ①繼承Thread類,實現run方法,呼叫start()方法開啟執行緒; ②實現Runnable介面,實現run方法, 呼叫start()方法開啟執行緒; 其實還有第三種常用的新增執行緒的方式: 是通過Callable和Future建立執行緒
1. 繼承Thread類新增執行緒
使用該方法新增執行緒的步驟是: 第一步:建立類繼承Thread類 第二步:實現run方法,將任務寫在run方法裡面 第三步:在main方法new出該類,呼叫start方法開啟執行緒
程式碼實現如下:
public class ThreadTest extends Thread { //實現run方法 @Override public void run() { for(int i=0;i<=6;i++) { System.out.println(Thread.currentThread().getName() + "--" + i); try { Thread.sleep(new Random().nextInt(100)); //休眠 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"--finsh"); } public static void main(String[] args) { new ThreadTest("A").start(); new ThreadTest("B").start(); } }
輸出結果為(結果不唯一):
Thread0–1 Thread1–1 Thread0–2 Thread0–3 Thread1–2 Thread1–3 Thread0–4 Thread0–5 Thread0–6 Thread0–finish Thread1–4 Thread1–5 Thread1–6 Thread1–finish
我們可以發現,繼承Thread類建立的執行緒可以擁有自己獨立的類成員變數i。
2.實現Runnable介面建立執行緒
使用該方法建立執行緒的步驟是: 第一步:建立類實現Runnable介面; 第二步:實現run方法,將任務寫在run方法內; 第三步:建立Thread物件,建立Runnable例項,將其傳入Thread物件中; 第四步:呼叫start方法開啟執行緒。
程式碼實現如下:
public class RunableTest implements Runnable{ private int j; //實現run方法 @Override public void run() { for(int i=0;i<=10;i++) { try { Thread.sleep(new Random().nextInt(100)); //休眠 j++; System.out.println(Thread.currentThread().getName()+"--"+j); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(name+" finsh"); } public static void main(String[] args) { RunableTest R=new RunableTest(); new Thread(R).start(); new Thread(R).start(); } }
執行結果為:
Thread0–1 Thread1–2 Thread0–3 Thread0–4 Thread1–5 Thread1–6 Thread0–7 Thread0–8 Thread0–9 Thread0–10
在加入休眠操作之後,可以發現,雖然執行緒不同,但是i卻是程序共享的。
3.通過Callable和Future建立執行緒
先簡單瞭解以下Callable和Future介面以及他的實現類FutureTask:
- Callable是一個介面,它與Runnable極其相似,但在Runnable介面中run方法是執行緒執行體,而在Callable中call方法是執行緒執行體。
- call能夠實現run能實現的所有功能,除此之外還多出以下兩個功能:
- call方法允許有返回值,可以在執行完後返回資料;
- call方法能夠宣告丟擲的異常;
- FutureTask類實現了Runnable和Future介面;
- Future介面是對Callable任務的執行結果進行取消,查詢是否完成。
使用該方法建立執行緒的步驟是: 第一步:建立類實現Callable介面; 第二步:實現call方法, 將任務放在call方法內; 第三步:建立Callable例項,建立FutureTask例項,將Callable例項傳入FutureTask中; 第四步:建立Thread物件,將FutureTask例項傳入Thread物件中; 第五步:呼叫start方法, 開啟執行緒。
程式碼實現如下:
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class CallableTest implements Callable<Integer> {
//實現call方法
@Override
public Integer call() throws Exception {
int i=0;
for(;i<10;i++) {
System.out.println(Thread.currentThread().getName()+ "--" + i);
try {
Thread.sleep(new Random().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("finsh");
return i;
}
public static void main(String[] args) {
CallableTest callable1 = new CallableTest();
CallableTest callable2 = new CallableTest();
FutureTask<Integer> furureTask1 = new FutureTask<Integer>(callable1);
FutureTask<Integer> futureTask2 = new FutureTask<Integer>(callable2);
new Thread(Task1).start();
new Thread(Task2).start();
}
}
輸出結果如下:
Thread0–1 Thread1–1 Thread0–2 Thread0–3 Thread1–2 Thread0–4 Thread0–5 Thread0–6 finish Thread1–3 Thread1–4 Thread1–5 Thread1–6 finish
在該輸出方法中,執行緒在執行過程中並不會輸出返回值,如果要獲得返回值則呼叫futureTask1.get()即可得到返回值。
4.區別:
三者區別在以上已經有所說明,總結來說有以下三點:
- 繼承Thead類建立的執行緒可以擁有獨立的成員變數,而實現Runnable介面建立的執行緒中的成員變數則是程序共享的;
- 三者的建立方式有所不同;
- 使用Callable和Task建立的執行緒的執行體與前兩者的執行體不同,前兩者是run方法作為執行體,後者是call方法作為執行體。call方法作為執行體有以下兩個優勢:1)call方法允許有返回值,可以在執行完後返回資料; 2) call方法能夠宣告丟擲的異常。