1. 程式人生 > >Java多執行緒(一):執行緒基礎及建立

Java多執行緒(一):執行緒基礎及建立

(一)、執行緒的生命週期

在這裡插入圖片描述

  • 新建狀態:

    使用 new 關鍵字和 Thread 類或其子類建立一個執行緒物件後,該執行緒物件就處於新建狀態。它保持這個狀態直到程式 start() 這個執行緒。

  • 就緒狀態:

    當執行緒物件呼叫了start()方法之後,該執行緒就進入就緒狀態。就緒狀態的執行緒處於就緒佇列中,要等待JVM裡執行緒排程器的排程。

  • 執行狀態:

    如果就緒狀態的執行緒獲取 CPU 資源,就可以執行 run(),此時執行緒便處於執行狀態。處於執行狀態的執行緒最為複雜,它可以變為阻塞狀態、就緒狀態和死亡狀態。

  • 阻塞狀態:

    如果一個執行緒執行了sleep(睡眠)、suspend(掛起)等方法,失去所佔用資源之後,該執行緒就從執行狀態進入阻塞狀態。在睡眠時間已到或獲得裝置資源後可以重新進入就緒狀態。可以分為三種:

    • **等待阻塞:**執行狀態中的執行緒執行 wait() 方法,使執行緒進入到等待阻塞狀態。
    • **同步阻塞:**執行緒在獲取 synchronized 同步鎖失敗(因為同步鎖被其他執行緒佔用)。
    • **其他阻塞:**通過呼叫執行緒的 sleep() 或 join() 發出了 I/O 請求時,執行緒就會進入到阻塞狀態。當sleep() 狀態超時,join() 等待執行緒終止或超時,或者 I/O 處理完畢,執行緒重新轉入就緒狀態。
  • 死亡狀態:

    一個執行狀態的執行緒完成任務或者其他終止條件發生時,該執行緒就切換到終止狀態。

(二)、執行緒的優先順序

每一個 Java 執行緒都有一個優先順序,這樣有助於作業系統確定執行緒的排程順序

Java 執行緒的優先順序是一個整數,其取值範圍是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。

預設情況下,每一個執行緒都會分配一個優先順序 NORM_PRIORITY(5)。

具有較高優先順序的執行緒對程式更重要,並且應該在低優先順序的執行緒之前分配處理器資源。但是,執行緒優先順序不能保證執行緒執行的順序,而且非常依賴於平臺。

(三)、建立執行緒的三種方式的對比

  • 採用實現 Runnable、Callable 介面的方式建立多執行緒時,執行緒類只是實現了 Runnable 介面或 Callable 介面,還可以繼承其他類。(常用方式
  • 使用繼承 Thread 類的方式建立多執行緒時,編寫簡單,如果需要訪問當前執行緒,則無需使用 Thread.currentThread() 方法,直接使用 this 即可獲得當前執行緒。

(四)、建立執行緒的三種方式

  • 通過繼承 Thread 類本身
  • 通過實現 Runnable 介面
  • 通過Callable 和 Future 建立執行緒

1. 繼承Thread ,重寫run()方法

//多執行緒:
// 1. 繼承Thread , 重寫run方法; 呼叫.start()方法進入就緒狀態
public class MyThread extends Thread{
	@Override
    //run()方法內 稱為執行緒執行體
	public void run() {
		for (int i = 0; i < 100; i++) {
			// 列印 執行緒名稱
			System.out.println(Thread.currentThread().getName()+">>>"+i);
		}
        System.out.println(Thread.currentThread().getName()+">>> 執行緒結束");
	}
}
public static void main(String[] args) {
	for (int i = 0; i < 100; i++) {
        //main 函式
		System.out.println(Thread.currentThread().getName()+"---"+i);
	}
    
   	Thread myThread1 = new MyThread();
    //呼叫start()方法使得執行緒進入就緒狀;執行緒不會立刻執行,執行取決於CPU排程時機
	myThread2.start();
}
  • Thread 重要方法
    在這裡插入圖片描述

  • Thread 靜態方法
    在這裡插入圖片描述

2. 實現 Runnable 介面,重寫run()方法

建立執行緒,最簡單的方法是建立一個實現 Runnable 介面的類。並實現Runnable介面中的run方法

public class MyRunnable implements Runnable{
	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName()+"---"+i);
		}
		System.out.println(Thread.currentThread().getName()+"--- 執行緒結束");
		
	}
		
	/*//Runnable 介面
	 * public abstract interface java.lang.Runnable {
		  
		  // Method descriptor #1 ()V
		  public abstract void run();

	}*/
}
public class Main {
	public static void main(String[] args) {
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName()+">>>"+i);
		}
       	//建立 Runnable物件 
       	Runnable myRunnable = new MyRunnable();
		//將myRunnable作為Thread target建立新的執行緒
		Thread thread = new Thread(myRunnable);
		thread.start();
			
	}
}

3. 使用Callable和Future介面建立執行緒

    1. 建立 Callable 介面的實現類,並實現 call() 方法,該 call() 方法將作為執行緒執行體,並且有返回值。
    1. 建立 Callable 實現類的例項,使用 FutureTask 類來包裝 Callable 物件,該 FutureTask 物件封裝了該 Callable 物件的 call() 方法的返回值。
    1. 使用 FutureTask 物件作為 Thread 物件的 target 建立並啟動新執行緒。
    1. 呼叫 FutureTask 物件的 get() 方法來獲得子執行緒執行結束後的返回值。
//建立Callable 介面實現類,並實現call()方法。
//該 call() 方法將作為執行緒執行體,並且有返回值。
public class CallableThread implements Callable<Integer>{

	//帶有返回值的 call() 方法
	@Override
	public Integer call() throws Exception {
		int i = 0 ;
		for (; i < 100; i++) {
			System.out.println(Thread.currentThread().getName()+"---"+i);
		}
		return i;
	}
}
public class Main {
	public static void main(String[] args) {
		//建立Callable介面實現類 物件
		CallableThread ct = new CallableThread();
		//建立 FutureTask物件 , 並傳入 Callable 物件
		FutureTask<Integer> ft = new FutureTask<>(ct);
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName()+">>>"+i);			
			
			//當i=20時,開啟Callable執行緒;並設定執行緒名稱
			if(i == 20){
				new Thread(ft,"有返回值的執行緒").start();
			}
		}
		
		//獲取Callable執行緒 call()方法的返回值
		try {
			System.out.println("子執行緒返回值:"+ft.get());
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}