1. 程式人生 > >三種Java建立執行緒的方式(Callable,FutureTask)

三種Java建立執行緒的方式(Callable,FutureTask)

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類建立執行緒
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的效果大體相同。