1. 程式人生 > >java多執行緒知識總結

java多執行緒知識總結

程序:一個程序包括由作業系統分配的記憶體空間,包含一個或多個執行緒。一個執行緒不能獨立的存在,它必須是程序的一部分。一個程序一直執行,直到所有的非守護執行緒都結束執行後才能結束。
多執行緒能滿足程式設計師編寫高效率的程式來達到充分利用 CPU 的目的。

詳細解釋:
程式:為了完成特定任務,用某種語言編寫的一組指令集合(一組靜態程式碼)
程序:執行中的程式,系統排程與資源分配的一個獨立單位,作業系統會 為每個程序分配一段記憶體空間!程式的依次動態執行,經歷程式碼的載入,執行, 執行完畢的完整過程!
執行緒:比程序更小的執行單元,每個程序可能有多條執行緒,執行緒需要放在一個 程序中才能執行,執行緒由程式負責管理,而程序則由系統進行排程!
多執行緒的理解

:並行執行多個條指令,將CPU時間片按照排程演算法分配給各個 執行緒,實際上是分時執行的,只是這個切換的時間很短,使用者感覺到"同時"而已!

執行緒的建立有三種:
1、繼承Thread類代表執行緒
定義Thread類的子類,並重寫run()方法,run()方法稱為執行緒的執行體;
建立Thread類的子類的例項,即建立執行緒的物件;
呼叫執行緒物件的start()方法啟動執行緒。
程式碼示例:

package thread;

public class TestThread {
	public static void main(String[] args) {
		SubThread1 st1 = new SubThread1();
		SubThread1 st2 = new SubThread1();
		st1.start();
		st2.start();
	}
}
class SubThread1 extends Thread{
	public void run(){
		for(int i = 0;i< 14;i++) {
			if(i % 2 == 0) {
				System.out.println(Thread.currentThread().getName()+" "+i);
			}
		}
	}
}

執行結果:

Thread-0 0
Thread-1 0
Thread-1 2
Thread-1 4
Thread-0 2
Thread-1 6
Thread-0 4

2、實現Runnable介面建立執行緒:
定義Runnable介面實現類,並重寫run()方法;
建立Runnable實現類的例項,並將例項物件傳給Thread類的target來建立執行緒物件;
呼叫執行緒物件的start()方法啟動執行緒。
程式碼示例:

package thread;

public class TestRunnable {
         public static void main(String[] args) {
			SubThread2 st = new SubThread2();
			new Thread(st,"thread1").start();
			new Thread(st,"thread2").start();
		}
}
class SubThread2 implements Runnable{
	public void run() {
		for(int i = 0;i < 8; i++) {
			if (i % 2 == 0) {
				System.out.println(Thread.currentThread().getName()+" "+i);
			}
		}	
	}
}

結果:

thread1:0
thread2:0
thread2:2
thread2:4
thread2:6
thread1:2

3、使用Callable介面和Future介面建立執行緒:
定義Callable介面實現類,指定返回型別,並重寫call()方法;
建立Callable介面實現類的例項,使用FutureTask類來包裝Callable物件,該FutureTask物件封裝了該Callable物件的call()方法的返回值;
使用FutureTask物件作為Thread物件的target建立並啟動新執行緒;
呼叫FutureTask 物件的get()方法來獲得子執行緒執行結束後的返回值。

程式碼示例:

package thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class TestCallable {
         public static void main(String[] args) {
			Callable<Integer>myCallable = new SubThread3();
			FutureTask<Integer> ft = new FutureTask<Integer>(myCallable);
			for(int i = 0;i < 4;i ++) {
				System.out.println(Thread.currentThread().getName()+" "+i);
				if(i % 2 == 1) {
					Thread thread = new Thread(ft);
					thread.start();
				}
			}
			System.out.println("主執行緒for迴圈執行完成");
			try {
				int sum = ft.get();
				System.out.println("sum ="+sum);
			}catch(InterruptedException e){
				e.printStackTrace();
			}catch(ExecutionException e) {
				e.printStackTrace();
			}
		}
}
class SubThread3 implements Callable<Integer>{
	private int i = 0;
	public Integer call()throws Exception{
		int sum = 0;
		for(i = 0;i<3;i++) {
			System.out.println(Thread.currentThread().getName()+" "+i);
			sum += i;
		}
		return sum;
	}
}

結果:

main 1
main 2
main 3
主執行緒for迴圈執行完成
Thread-1 0
Thread-1 1
Thread-1 2
sum =3

總結:
建立執行緒必須要通過Thread類的例項或Thread類子類的例項,然後呼叫start()方法啟動執行緒,上述三種方式可以分為兩種:1是呼叫無參的建構函式Thread()來例項化物件;2、3是呼叫Thread(Runnable target)構造方法,其中引數為Runnable類的例項化物件,在3中使用了FutureTask(FutureTask實現了Runnable介面),因此可以看做FutureTask的例項化物件是Runnable型別的。

建立執行緒的三種方式的對比:

  1. 採用實現 Runnable、Callable 介面的方式建立多執行緒時,執行緒類只是實現了 Runnable 介面或 Callable 介面,還可以繼承其他類。

  2. 使用繼承 Thread 類的方式建立多執行緒時,編寫簡單,如果需要訪問當前執行緒,則無需使用 Thread.currentThread() 方法,直接使用 this 即可獲得當前執行緒。

  3. 使用Callable 介面的方式有返回值,可以丟擲異常。

執行緒的生命週期和狀態轉換
在這裡插入圖片描述
1、新建狀態:
使用 new 關鍵字和 Thread 類或其子類建立一個執行緒物件後,該執行緒物件就處於新建狀態。它保持這個狀態直到程式 start() 這個執行緒。此時執行緒物件在堆空間中分配了一塊記憶體,但還不能執行。

2、就緒狀態:
當執行緒物件呼叫了start()方法之後,該執行緒就進入就緒狀態。就緒狀態的執行緒處於就緒佇列中,java虛擬機器會 為它建立呼叫棧和程式計數器,處於這個狀態 的執行緒位於可執行池中,等待獲得CPU的使用權。

3、執行狀態:
如果就緒狀態的執行緒獲取 CPU 資源,就可以執行 run(),此時執行緒便處於執行狀態,每個CPU只能被一個執行緒佔用。只有處於執行狀態的執行緒才可以轉換到執行狀態,處於執行狀態的執行緒最為複雜,它可以變為阻塞狀態、就緒狀態和死亡狀態。

4、阻塞狀態:
如果一個執行緒執行了sleep(睡眠)、suspend(掛起)等方法,失去所佔用CPU等資源之後,該執行緒就從執行狀態進入阻塞狀態,進入阻塞狀態java虛擬機器不會給執行緒分配CPU,直到執行緒重新進入就緒狀態才有可能分配到CPU,再次進入執行狀態。可以分為三種:

等待阻塞:執行狀態中的執行緒執行 wait() 方法,使執行緒進入到等待阻塞狀態。

同步阻塞:執行緒在獲取 synchronized 同步鎖失敗(因為同步鎖被其他執行緒佔用)。

其他阻塞:通過呼叫執行緒的 sleep() 或 join() 發出了 I/O 請求時,執行緒就會進入到阻塞狀態。當sleep() 狀態超時,join() 等待執行緒終止或超時,或者 I/O 處理完畢,執行緒重新轉入就緒狀態。

5、死亡狀態:
一個執行狀態的執行緒完成任務或者其他終止條件發生時,該執行緒就切換到終止狀態。如改執行緒的run()方法再次執行完成,執行緒正常結束;執行緒丟擲異常或錯誤;呼叫執行緒的stop()方法結束該執行緒。一旦執行緒轉換為死亡狀態就不能執行且不能轉換為其他狀態。

執行緒的排程:
由於一個CPU只能執行一個執行緒,在執行池中會有多個處於就緒狀態的執行緒在等待CPU,Java虛擬機器負責執行緒的排程,即按照特定的機制為多個執行緒分配CPU使用權。排程模型分為分時排程模型和搶佔式排程模型兩種。