Java多執行緒(一):執行緒基礎及建立
阿新 • • 發佈:2018-12-10
(一)、執行緒的生命週期
-
新建狀態:
使用 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介面建立執行緒
-
- 建立 Callable 介面的實現類,並實現 call() 方法,該 call() 方法將作為執行緒執行體,並且有返回值。
-
- 建立 Callable 實現類的例項,使用 FutureTask 類來包裝 Callable 物件,該 FutureTask 物件封裝了該 Callable 物件的 call() 方法的返回值。
-
- 使用 FutureTask 物件作為 Thread 物件的 target 建立並啟動新執行緒。
-
- 呼叫 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();
}
}
}