Java進階——執行緒與多執行緒
執行緒和多執行緒
概念
-
程式
程式是一段靜態程式碼。 -
程序
程序是程式的一次動態執行過程(從程式碼載入、執行、執行完畢的完整過程)。程序是資源分配的最小單位。 -
執行緒
執行緒是CPU排程的最小執行單位。程式執行過程中可以產生多個執行緒。
程序和執行緒的區別
- 對程序:一個應用程式對應一個程序;程序是資源分配的最小單位;通過多執行緒佔據系統資源;程序之間資料狀態完全獨立。
- 對執行緒:一個程序可以有多個執行緒;執行緒是執行程式的最小單元;執行緒是佔用CPU的基本單位;執行緒之間共享一塊記憶體空間。
執行緒的生命週期
-
新建狀態
執行緒物件建立,還未呼叫start()
方法。 -
就緒狀態
start()
方法,但排程程式還未將其選為可執行執行緒。 -
執行狀態
執行緒排程程式從可執行池中選擇一個執行緒作為當前執行緒。 -
阻塞狀態
- 等待狀態
- 阻塞狀態
- 睡眠狀態
執行緒是活的,但沒有條件執行;當某事件發生後,可返回到就緒狀態。
-
死亡狀態
執行緒run()
方法完成。執行緒一旦死亡,不可復生(呼叫start()會拋異常
)。
Java的執行緒
主執行緒
每個Java程式都有一個預設的主執行緒。
當JVM載入程式碼,發現main方法後,會立即啟動一個執行緒(主執行緒)
主執行緒特點
- 產生其他子執行緒的執行緒
- 不一定是最後完成執行的執行緒
建立執行緒
- 繼承Thread類
- 重寫
run()
- new一個執行緒物件
- 呼叫物件的
start()
方法啟動執行緒
- 重寫
- 實現Runnable介面
- 實現
run()
方法 - 建立一個Runnable類的物件
- 建立Thread類物件,將Runnable物件作為引數
- 呼叫Thread物件的
start()
方法啟動執行緒
- 實現
- 一個執行緒只能被啟動一次,
run()
方法執行結束後,執行緒結束。 - 一個程式多個執行緒,執行緒只能保證開始時間,結束時間和執行順序無法確定。
- 執行緒排程採用佇列形式;JVM執行緒排程程式決定執行就緒狀態的某個執行緒。
- 執行的執行緒有名字
- 可通過JVM預設執行緒名字
- 自定義執行緒名字
setName()
//給子執行緒命名 //預設為Thread- Thread1 t1=new Thread1(); Thread t=new Thread(t1); t.start(); t.setName("t1"); //獲取主執行緒,並命名 Thread.currentThread().setName("mm"); System.out.println(Thread.currentThread().getName());
建立執行緒方法對比
- 繼承Thread類
- 編寫簡單,訪問當前執行緒
this
- java是單繼承機制,不能繼承其他父類;沒有達到資源共享
- 編寫簡單,訪問當前執行緒
- 實現Runnable介面
- 程式設計複雜,訪問當前執行緒
Thread.currentThread()
- java是多介面實現,可繼承其他類;可多個執行緒共享同一個目標物件。
- 程式設計複雜,訪問當前執行緒
//Runnable
//多執行緒解決同一問題
Thread1 t1 = new Thread1();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
//Thread
//資源不共享
Thread t1 = new Thread1();
Thread t2 = new Thread2();
Thread t3 = new Thread3();
t1.start();
t2.start();
t3.start();
執行緒的方法
方法名 | 功能 |
---|---|
start() | 啟動執行緒,讓執行緒從新建狀態進入就緒狀態佇列 |
run() | 普通方法,執行緒物件被排程後執行的操作 |
sleep() | 暫停執行緒的執行,休眠執行緒 |
yield() | 暫停正在執行的執行緒,讓同等優先順序的執行緒執行 |
join() | 暫停當前執行緒的執行,等呼叫該方法的執行緒執行完後,執行緒返回就緒狀態 |
interrupt() | 喚醒休眠的執行緒 |
stop() | 終止執行緒 |
isAlive() | 測試執行緒的狀態;新建/死亡狀態=false |
currentThread() | 返回當前正在執行執行緒物件的引用 |
//輸出 (新建狀態、死亡狀態為false)
//false false
//join方法將主執行緒暫停,先執行Thread1執行緒
Thread1 t1=new Thread1();
Thread t=new Thread(t1);
System.out.print(t.isAlive());
t.start();
t.join();
System.out.print(t.isAlive());
設定執行緒優先順序
- 通過Thread的
setPriority()
方法設定執行緒優先順序Thread.MIN_PRIORITY
——1Thread.NORM_PRIORITY
——5Thread.MAX_PRIORUTY
——10
- 通過thread的
getPriority()
方法得到執行緒優先順序 - 執行緒預設優先順序為建立其的執行狀態執行緒的優先順序
執行緒讓步
當執行緒池中的執行緒具有相同優先順序:
- 選擇一個執行緒執行,知道執行緒阻塞或執行結束
- 時間分片,為執行緒池中每個執行緒提供均等執行機會
多執行緒執行時,JVM按優先順序排程,級別相同的由作業系統按時間片分配。
阻止執行緒執行
執行緒睡眠 sleep()
當執行緒睡眠時,暫停執行;甦醒前不會回到就緒狀態;
當睡眠時間到期,執行緒回到就緒狀態。
- 執行緒睡眠可幫助其他執行緒獲得執行機會的最好方法
- 執行緒甦醒後,返回到就緒狀態
sleep()
指定時間為最短睡眠時間sleep()
為靜態方法,只能控制當前執行的執行緒
執行緒等待 yield()
執行緒讓步,暫停當前正在執行的執行緒物件,並執行同等優先順序的其他執行緒
yield()使執行緒從執行狀態——>就緒狀態;讓步的執行緒也有可能被執行緒排程程式選中。
執行緒阻塞 join()
執行緒A中呼叫執行緒B的join()
方法,讓執行緒A置於執行緒B的尾部。
線上程B執行完畢之前,執行緒A一直處於阻塞狀態,只有當B執行緒執行完畢時,A執行緒才能繼續執行
當join(100)帶有引數時,如果A執行緒中掉用B執行緒的join(100),則表示A執行緒會等待B執行緒執行100毫秒,100毫秒過後,A、B執行緒並行執行;同時join(0)==join()
join方法必須線上程start方法呼叫之後呼叫才有意義
在主執行緒中執行程式:建立A、B兩個子執行緒,首先呼叫執行緒A的
start()
方法執行執行緒A;
呼叫執行緒A的join()
方法,使主執行緒進入阻塞狀態,只有當執行緒A執行完畢後,才能執行主執行緒
執行緒A執行完畢後,主執行緒才可執行,呼叫執行緒B的start()
方法執行執行緒B。
Thread a = new ThreadA();
Thread b = new ThreadB();
//執行緒A開始執行
a.start();
//執行緒A呼叫join()
a.join();
//執行緒B開始執行
b.start();
多執行緒
物件互斥鎖
Java每個物件都對應一個互斥鎖的標記。
每個物件只有一個鎖(lock)與之相關聯.
synchronized
關鍵字與物件互斥鎖聯合使用,保證物件在任意時刻只能由一個執行緒訪問。
避免多個執行緒進行訪問導致資料不同步的問題。
- 修飾程式碼塊,該程式碼塊在任意時刻只能由一個執行緒訪問
- 作用範圍:程式碼塊{}的內容
- 作用物件:呼叫該程式碼塊的物件
//實現Runnable介面
public class Thread1 implements Runnable {
private static int count;
@Override
public void run() {
// TODO Auto-generated method stub
// 1. 修飾程式碼塊
// 同步語句塊
synchronized (this) {
for (int i = 0; i < 5; i++) {
count++;
System.out.println(Thread.currentThread().getName() + ":" + count);
}
}
}
}
// 同一個物件
//實現資源共享、資源同步
Thread1 thread = new Thread1();
new Thread(thread).start();
new Thread(thread).start();
// 不同物件
//實現資源同步、多執行緒進行處理
Thread1 thread = new Thread1();
Thread1 thread1 = new Thread1();
new Thread(thread).start();
new Thread(thread1).start();
- 修飾方法,表示該方法在任意時刻只能由一個執行緒訪問
- 作用範圍:方法內容
- 作用物件:呼叫方法的物件
- 關鍵字
synchronized
不可繼承 - 定義介面不可使用關鍵字
synchronized
修飾 - 構造方法不可使用關鍵字
synchronized
修飾,但可以用同步程式碼塊
public synchronized void print(){
//todo
}
- 修飾靜態方法,
- 作用範圍:靜態方法內容
- 作用物件:這個類的所有物件
public synchronized static void print(){
//todo
}
- 修飾類,表示該類的所有物件公用一把鎖
- 作用範圍:{}包括的所有內容
- 作用物件:這個類的所有物件
class ClassTest{
public void method(){
synchronized(ClassTest.class){
//todo
}
}
}
多執行緒同步
為了更好的解決多個互動執行緒之間的執行進度。
引入wait()
方法與notify()
方法
wait()方法:使當前執行緒進行等待狀態
notify()方法:通知那些等待該物件鎖的其他執行緒,使其重新獲取該物件的物件鎖。
wait()
與notify()
方法必須配合synchronized
關鍵字使用wait()
會釋放鎖,notify()
不會釋放鎖wait()
方法執行後,執行interrupt()
方法會報異常
死鎖
死鎖:當兩個或兩個以上的執行緒在執行過程中時,因爭奪資源造成互相等待,若無外力作用,執行緒都無法推進下去的現象。
必要條件:
- 互斥條件
- 執行緒對分配到的資源進行排他性使用;其他執行緒不可使用。
- 請求、保持條件
- 執行緒保持至少一個資源,但又提出新的資源請求。
- 不可剝奪條件
- 執行緒獲得的資源在使用完之前,不可被剝奪;只能在使用完後自主釋放。
- 環路等待條件
- 發生死鎖時,必然存線上程請求資源、資源被另一執行緒佔用的環。
執行緒池
[java 執行緒方法join的簡單總結][join]
[Java中Runnable和Thread的區別][thread]
[Java中Synchronized的用法][synchronized]
[join]:https://www.cnblogs.com/lcplcpjava/p/6896904.html
[thread]:https://developer.51cto.com/art/201203/321042.htm
[synchronized]:https://www.cnblogs.com/fnlingnzb-learner/p/10335662.html