三種Java建立執行緒的方式(Callable,FutureTask)
阿新 • • 發佈:2019-02-17
Java執行緒具有併發性和非同步性,可以說執行緒是輕量級別的程序,java中執行緒和現代作業系統中的程序排程都是採用採用搶佔式執行。但執行緒和程序最大的區別是:一個程序中的多個程序共享這個程序的記憶體空間和系統資源,但是程序之間是有獨立的程式碼段和資料段。
下面介紹三種Java建立執行緒的方式:
1. 通過繼承Thread類建立執行緒
2. 通過實現Runnable介面來建立執行緒
3. 通過實現Callable介面利用TaskFutrue來建立執行緒,當然也可以配合ExecutorServic來使用 接著說明下這三種方式建立執行緒的區別: 1.繼承Thread類建立的執行緒可以擁有自己獨立的類成員變數,但是實現Runnable介面建立執行緒共享實現介面類的成員變數。兩中方式建立執行緒都要重寫run方法,run方法是執行緒的執行體。
2.在繼承Thread類建立程序中可以通過使用this獲得當前程序的物件,但是在實現Runnable介面建立執行緒的途徑中可以使用Thread.currentThread()方式來獲得當前程序。
3.第三中方式是較為複雜的一種。Callable介面是一個與Runnable介面十分相似的介面。在Runnable中run方法為執行緒的執行體,但是在Callable介面中call方法是執行緒的執行體。下面是兩個介面實現執行體的不同:
(1). call方法有返回值,但是run方法沒有
(2). call方法可以生命丟擲異常
所以可以說Callable介面是Runnable介面的增強版本
4.FutureTask類實現了Runnable和Future介面。和Callable一樣都是泛型。
5.Future介面是對Callable任務的執行結果進行取消,查詢是否完成,獲取結果的。下面是這個介面的幾個重要方法:
(1). boolean cancel(boolean myInterruptRunning)
試圖取消Future與Callable關聯的任務
(2). V get()
返回Callable任務中call方法的返回值,呼叫該方法會導致程式阻塞,必須等到子執行緒結束才會有
返回值。這裡V表示泛型
(3). V get(long timeout, TimeUnit unit)
返回Callable中call方法的返回值,該方法讓程式最多阻塞timeout毫秒的時間,或者直到unit時間
點。如果在指定的時間Callable的任務沒有完成就會丟擲異常TimeoutEexception
(4). boolean isCancelled()
如果Callable中的任務被取消,則返回true,否則返回false
(5). boolean isDone()
如果Callable中的任務被完成,則返回true,否則返回false
程式碼演示說明:
1. 通過繼承Thread類建立執行緒
2. 通過實現Runnable介面來建立執行緒
3.通過實現Callable介面利用TaskFutrue來建立執行緒
1. 通過繼承Thread類建立執行緒
2. 通過實現Runnable介面來建立執行緒
3. 通過實現Callable介面利用TaskFutrue來建立執行緒,當然也可以配合ExecutorServic來使用 接著說明下這三種方式建立執行緒的區別: 1.繼承Thread類建立的執行緒可以擁有自己獨立的類成員變數,但是實現Runnable介面建立執行緒共享實現介面類的成員變數。兩中方式建立執行緒都要重寫run方法,run方法是執行緒的執行體。
public class MyThread extends Thread {
private int count ; //每個新程序類都有獨立的成員變數count
@Override
public void run() { //重寫run方法
for (count = 1; count <= 5; count++) {
System.out.println(getName() + " :" + count);
}
}
public static void main(String[] agrs) {
for (int j = 1; j <= 10; j++) {
System.out.println(Thread.currentThread().getName()+" :"+j);
if(j == 5){
new MyThread().start();
new MyThread().start(); //倆個子執行緒和主執行緒併發執行
}
}
}
}
執行結果:(輸出結果不唯一)每個子執行緒都輸出5次,主執行緒輸出10次,因子程序通過繼承Thread類建立,擁有自己獨立的成員變數count=5;main :1
main :2
main :3
main :4
main :5
main :6
Thread-0 :1
main :7
main :8
main :9
main :10
Thread-1 :1
Thread-0 :2
Thread-0 :3
Thread-0 :4
Thread-1 :2
Thread-0 :5
Thread-1 :3
Thread-1 :4
Thread-1 :5
2. 通過實現Runnable介面來建立執行緒
public class NewThread implements Runnable {
private int count; //用同一個NewThread類開闢的不同執行緒共享同一個類的成員變數
@Override
public void run() {
for(count = 1; count < 5; count++){
System.out.println(Thread.currentThread().getName() + " :" + count);
} //利用Thread.currentThread()獲得當前程序
}
public static void main(String[] agrs){
NewThread nt = new NewThread();
for (int j = 1; j <= 10; j++) {
System.out.println(Thread.currentThread().getName()+" :"+j);
if(j == 5){
new Thread(nt).start();
new Thread(nt).start(); //倆個子執行緒和主執行緒併發執行
}
}
}
}
執行結果:(輸出結果不唯一),通過同一個實現Runnable介面的類建立多個子執行緒共享該類的成員變數count
= 5,所以兩個子執行緒Thread-0 和Thread-1一共輸出5次,主執行緒輸出10次。main :1
main :2
main :3
main :4
main :5
main :6
main :7
Thread-0 :1
main :8
Thread-1 :1
main :9
Thread-0 :2
main :10
Thread-1 :2
Thread-0 :3
Thread-1 :4
3.通過實現Callable介面利用TaskFutrue來建立執行緒
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class CallableThread implements Callable<String> {
private boolean flag;
public CallableThread(boolean f) {
this.flag = f;
}
@Override
public String call() throws Exception {
for (int i = 1; i <= 5; i++) {
//執行輸出5次,因為call方式是阻塞方式的,所以這五次輸出應該是連續
System.out.println(Thread.currentThread().getName() + " :" + i);
}
return flag == true ? "flag is true" : "flag is false";
}
}
public class FutureThread {
public static void main(String[] agrs) {
CallableThread ct1 = new CallableThread(false);
CallableThread ct2 = new CallableThread(true);
CallableThread ct3 = new CallableThread(false);
FutureTask<String> task1 = new FutureTask<String>(ct1);
FutureTask<String> task2 = new FutureTask<String>(ct2);
FutureTask<String> task3 = new FutureTask<String>(ct3);
Thread thread1 = new Thread(task1); //建立第一個執行緒
Thread thread2 = new Thread(task2); //建立第二個執行緒
Thread thread3 = new Thread(task3); //建立第三個執行緒
for(int i = 1; i <= 10; i++){
System.out.println(Thread.currentThread().getName()+" :"+i);
if(i == 5){
try{
thread1.start(); //啟動第一個執行緒
System.out.println("task1 get value: "+task1.get());
thread2.start(); //啟動第二個執行緒
System.out.println("task2 get value: "+task2.get());
thread3.start(); //啟動第三個執行緒
System.out.println("task3 get value: "+task3.get());
}catch(Exception e){
e.printStackTrace();
}//每個執行緒的task呼叫get方法的時候都會阻塞程式,所以會連續輸出5次後執行後面的程式
}
}
}
}
執行結果:(可能不唯一,但是Thread-0 :1到task3 get value: flag is false之間的輸出是相同的,具體原因就是FutureTask的get()方法會使程式阻塞)
<span style="font-size:18px;">main :1
main :2
main :3
main :4
main :5
Thread-0 :1
Thread-0 :2
Thread-0 :3
Thread-0 :4
Thread-0 :5
task1 get value: flag is false
Thread-1 :1
Thread-1 :2
Thread-1 :3
Thread-1 :4
Thread-1 :5
task2 get value: flag is true
Thread-2 :1
Thread-2 :2
Thread-2 :3
Thread-2 :4
Thread-2 :5
task3 get value: flag is false
main :6
main :7
main :8
main :9
main :10</span>
對於Callable配合ExecutorService使用建立執行緒請參考我的下一篇博文,實現的效果跟Callable配合FutureTask的效果大體相同。