1. 程式人生 > 實用技巧 >java執行緒三種建立方式

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類,所以不能再繼承其他父類。