java執行緒三種建立方式
Java中建立執行緒主要有三種方式:
一、繼承Thread類建立執行緒類(Thread 是類,且實現了Runnable介面)
(1)定義Thread類的子類,並重寫該類的run方法,該run方法的方法體就代表了執行緒要完成的任務。因此把run()方法稱為執行體。
(2)建立Thread子類的例項,即建立了執行緒物件。
(3)呼叫執行緒物件的start()方法來啟動該執行緒。
方法 | 說明 |
void run() | 執行操作任務的方法 |
void start() | 使該執行緒開始執行 |
void sleep(long millis) | 在指定的毫秒內讓當前正在執行的執行緒休眠(暫停執行) |
String getName() | 返回該執行緒的名稱 |
int getPriority() | 返回該執行緒的優先順序 |
void setPriority(int newPriority) | 更改該執行緒的優先順序 |
Thread.state getState() | 返回該執行緒的狀態 |
boolean isAlive() | 測試執行緒是否處於活動狀態 |
void join() | 等待該執行緒終止 |
void interrupt() | 中斷執行緒 |
void yieid() |
暫停當前正在執行的執行緒物件,並執行其他執行緒 |
如:
public class MyThread extends Thread { private int count=0; public void run() { while (count<100) { count++; System.out.println("count值:"+count); } } }
public class MyThreadTest {
public static void main(String[] args)
{
MyThread thread1=new MyThread();
thread1.start();
MyThread thread2=new MyThread();
thread2.start();
}
}
上述程式碼中Thread.currentThread()方法返回當前正在執行的執行緒物件。GetName()方法返回呼叫該方法的執行緒的名字。
二、通過Runnable介面建立執行緒類
1避免繼承的侷限,一個類可以繼承多個介面
具體什麼缺陷呢?
①首先來從介面實現和類繼承的區別來談談
如果你想寫一個類C,但這個類C已經繼承了一個類A,此時,你又想讓C實現多執行緒。用繼承Thread類的方式不行了。(因 為單繼承的侷限性),此時,只能用Runnable介面,Runnable介面就是為了解決這種情境出現的
2 適合於資源的共享
(1)定義runnable介面的實現類,並重寫該介面的run()方法,該run()方法的方法體同樣是該執行緒的執行緒執行體。
(2)建立 Runnable實現類的例項,並依此例項作為Thread的target來建立Thread物件,該Thread物件才是真正的執行緒物件。
(3)呼叫執行緒物件的start()方法來啟動該執行緒。
例項Runnable
public class MyRunnable implements Runnable{
private int tickets = 3;
@Override
public void run()
{
while (tickets > 0) {
tickets--; // 如果還有票就賣一張
System.out.println("剩餘票數為:" + tickets);
}
}
}
測試:
MyRunnable thread = new MyRunnable();
new Thread(thread).start();//同一個mt,但是在Thread中就不可以,如果用同一
new Thread(thread).start();//個例項化物件mt,就會出現異常
new Thread(thread).start();
結果:
剩餘票數為:1
剩餘票數為:1
剩餘票數為:0
例項Thread
public class MyThread extends Thread {
private int tickets =3;
@Override
public void run()
{
while (tickets > 0) {
tickets--; // 如果還有票就賣一張
System.out.println("剩餘票數為:" + tickets);
}
}
}
測試:
MyThread mts = new MyThread();
new Thread(mts).start(); //啟動 n 個執行緒
MyThread mts2 = new MyThread();
new Thread(mts2).start();
MyThread mts3 = new MyThread();
new Thread(mts3).start();
結果:
剩餘票數為:2
剩餘票數為:2
剩餘票數為:1
剩餘票數為:0
剩餘票數為:1
剩餘票數為:0
剩餘票數為:2
剩餘票數為:1
剩餘票數為:0
總結:
Thread類也是Runnable介面的子類,可見, 實現Runnable介面相對於繼承Thread類來說,有如下顯著的好處:
-
適合多個相同程式程式碼的執行緒去處理同一資源的情況,把虛擬CPU(執行緒)同程式的程式碼,資料有效的分離,較好地體現了面向物件的設計思想。
-
可以避免由於Java的單繼承特性帶來的侷限。我們經常碰到這樣一種情況,即當我們要將已經繼承了某一個類的子類放入多執行緒中,由於一個類不能同時有兩個父類,所以不能用繼承Thread類的方式,那麼,這個類就只能採用實現Runnable介面的方式了。
-
有利於程式的健壯性,程式碼能夠被多個執行緒共享,程式碼與資料是獨立的。當多個執行緒的執行程式碼來自同一個類的例項時,即稱它們共享相同的程式碼。多個執行緒操作相同的資料,與它們的程式碼無關。當共享訪問相同的物件是,即它們共享相同的資料。當執行緒被構造時,需要的程式碼和資料通過一個物件作為建構函式實參傳遞進去,這個物件就是一個實現了Runnable介面的類的例項
三、通過Callable和Future建立執行緒
(1)建立Callable介面的實現類,並實現call()方法,該call()方法將作為執行緒執行體,並且有返回值。
(2)建立Callable實現類的例項,使用FutureTask類來包裝Callable物件,該FutureTask物件封裝了該Callable物件的call()方法的返回值。
(3)使用FutureTask物件作為Thread物件的target建立並啟動新執行緒。
(4)呼叫FutureTask物件的get()方法來獲得子執行緒執行結束後的返回值
例項程式碼:
/*
建立Callable介面的實現類,並實現clall()方法
*/
public class MyCallable implements Callable<Integer> {
private int i = 0;
// 與run()方法不同的是,call()方法具有返回值
@Override
public Integer call() {
int sum = 0;
for (; i < 50; i++) {
// System.out.println(Thread.currentThread().getName() + " " + i);
sum += i;
}
return sum;
}
}
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class MyCallableText {
public static void main(String[] args) {
//並使用FutureTask類來包裝Callable實現類的物件,且以此FutureTask物件作為Thread物件的target來建立執行緒
Callable<Integer> myCallable = new MyCallable(); // 建立MyCallable物件
FutureTask<Integer> ftk = new FutureTask<Integer>(myCallable); //使用FutureTask來包裝MyCallable物件
for (int i = 0; i < 50; i++) {
// System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 30) {
Thread thread = new Thread(ftk); //FutureTask物件作為Thread物件的target建立新的執行緒
thread.start(); //執行緒進入到就緒狀態
}
}
System.out.println("主執行緒迴圈執行完畢..");
int sum = 0; //取得新建立的新執行緒中的call()方法返回的結果
try {
sum = ftk.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("sum = " + sum);
}
}
結果:
主執行緒迴圈執行完畢..
sum = 1225
四、建立執行緒的三種方式的對比
採用實現Runnable、Callable介面的方式創見多執行緒時,
Runnable和Callable的區別是,
(1)Callable規定的方法是call(),Runnable規定的方法是run()。
(2)Callable的任務執行後可返回值,而Runnable的任務是不能返回值得
(3)call方法可以丟擲異常,run方法不可以
(4)執行Callable任務可以拿到一個Future物件,表示非同步計算的結果。
優勢是:
1 執行緒類只是實現了Runnable介面或Callable介面,還可以繼承其他類。
在這種方式下,多個執行緒可以共享同一個target物件,所以非常適合多個相同執行緒來處理同一份資源的情況,從而可以將CPU、程式碼和資料分開,形成清晰的模型,較好地體現了面向物件的思想。
2與 Runnable 相比,Callable 可以有返回值,返回值通過 FutureTask 進行封裝。
劣勢是:
程式設計稍微複雜,如果要訪問當前執行緒,則必須使用Thread.currentThread()方法。
使用繼承Thread類的方式建立多執行緒時優勢是:
編寫簡單,如果需要訪問當前執行緒,則無需使用Thread.currentThread()方法,直接使用this即可獲得當前執行緒。
Thread劣勢是:
執行緒類已經繼承了Thread類,所以不能再繼承其他父類。