java多執行緒知識總結
程序:一個程序包括由作業系統分配的記憶體空間,包含一個或多個執行緒。一個執行緒不能獨立的存在,它必須是程序的一部分。一個程序一直執行,直到所有的非守護執行緒都結束執行後才能結束。
多執行緒能滿足程式設計師編寫高效率的程式來達到充分利用 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型別的。
建立執行緒的三種方式的對比:
-
採用實現 Runnable、Callable 介面的方式建立多執行緒時,執行緒類只是實現了 Runnable 介面或 Callable 介面,還可以繼承其他類。
-
使用繼承 Thread 類的方式建立多執行緒時,編寫簡單,如果需要訪問當前執行緒,則無需使用 Thread.currentThread() 方法,直接使用 this 即可獲得當前執行緒。
-
使用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使用權。排程模型分為分時排程模型和搶佔式排程模型兩種。