執行緒基礎與狀態
?生命不息,寫作不止
? 繼續踏上學習之路,學之分享筆記
? 總有一天我也能像各位大佬一樣
? 一個有夢有戲的人 @怒放吧德德
?分享學習心得,歡迎指正,大家一起學習成長!
前言
好久沒堅持學習了,所以,這次要好好下定決心學習。多執行緒與高併發不是一天兩天就能弄懂的,需要不斷的學習、實踐,本次筆者將最近學習的內容知識記錄下來。多執行緒也是一項比較重要的內容,雖然CRUD不太會接觸到,但是,在一些相關場景可能會有某些問題是由於執行緒導致的。
執行緒的概念
要了解執行緒的概念,就需要知道什麼是程式。簡單理解就是一個程式中包含了許多個執行緒。現在就簡單介紹,後續若是有對作業系統進行研究的話會慢慢介紹,具體關於執行緒程式的內容可以去看看王道的作業系統,裡面講述得特別清楚。
什麼是程式?
是系統進行資源分配的基本單位,是作業系統結構的基礎,程式是執行緒的容器。程式是指令、資料及其組織形式的描述,程式是程式的實體。
什麼是執行緒?
所謂執行緒就是作業系統(OS)能夠進行運算排程的最小單位,是一個基本的CPU執行單元,也是執行程式流的最小單元。能夠提高OS的併發效能,減小程式在併發執行時所付出的時空開銷。執行緒是程式的一個實體,是被系統獨立排程和分派的基本單位。執行緒本身是不擁有系統資源的,但是它能夠使用同屬程式的其他執行緒共享程式所擁有的全部資源。
在Java執行緒中是怎樣的呢?
在Java中,最常見得就是繼承Thread類或者實現Runnable介面,再透過run或者start方法去執行執行緒。
如以下程式碼,這是一段很簡單得程式碼塊,透過繼承Thread類,重寫run方法來建立執行緒,並且透過run和start來執行。
public class Thread_demo01 {
private static class Thread1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("Thread1-" + i);
}
}
}
public static void main(String[] args) {
new Thread1().run(); // 順序執行
// new Thread1().start(); // 執行緒同時執行
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("main");
}
}
}
這兩種不同得啟動方式,出現得現象也是不同的。
- 使用run方法啟動執行緒
- 使用start方法啟動執行緒
關於這兩種方式啟動執行緒可以看一下下面的流程圖。
run方法是會讓執行緒T1先執行完畢之後,再繼續執行主執行緒,而start方法他是同時執行兩個執行緒。
Java執行緒的Sleep、Yield、Join方法
1、sleep方法
sleep()需要提供一個時間引數(毫秒),會使得執行緒在一定的時間內被暫停執行,在sleep的過程中,執行緒是不會釋放鎖的,只會進入阻塞狀態,讓出cpu給其他執行緒去執行。如下程式碼演示,此處不做鎖的探究。
public class T3_Thread_Sleep {
public static void main(String[] args) {
Thread t1 = new Thread(new Thread1());
Thread t2 = new Thread(new Thread2());
t1.start();
t2.start();
}
static class Thread1 implements Runnable {
@Override
public void run() {
System.out.println("T1 is running");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("T1 is end");
}
}
static class Thread2 implements Runnable {
@Override
public void run() {
System.out.println("T2 is running");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("T2 is end");
}
}
}
執行之後在T1執行緒休眠的時候會讓出cpu資源給T2執行緒,T1執行緒會睡眠1秒,T2睡眠2秒,最終結果如圖
2、Yield方法
yield()與sleep()都是讓執行緒暫停執行,也是不會釋放鎖資源。但是yield並不是讓程式進入阻塞態,而是回到就緒態,等待重新獲取CPU資源。此時,其他的執行緒有機會獲得cpu資源,也有可能在yield方法進入就緒態後立馬變成執行態。如以下程式碼,同樣不考慮鎖的問題。
public class T4_Thread_Yield {
static class Thread1 implements Runnable {
@Override
public void run() {
System.out.println("T1 is running");
Thread.yield();
System.out.println("T1 is end");
}
}
static class Thread2 implements Runnable {
@Override
public void run() {
System.out.println("T2 is running");
System.out.println("T2 is end");
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new Thread1());
Thread t2 = new Thread(new Thread2());
t1.start();
t2.start();
}
}
經過不同的測試,結果都是不同的。
3、Join方法
join()方法是暫停當前執行緒,呼叫執行另一個執行緒,等待join的執行緒執行完畢後才能夠繼續執行當前執行緒。如以下例子,T1,T2同時開始,在T2執行緒中join了T1,就會導致T1要先執行完畢之後,才會去執行T2。
public class T5_Thread_Join {
public static void main(String[] args) {
Thread T1 = new Thread(() -> {
System.out.println("T1開始");
for (int i = 0; i < 5; i++) {
System.out.println("執行緒T1執行中: " + i);
}
System.out.println("T1結束");
});
Thread T2 = new Thread(() -> {
System.out.println("T2開始");
for (int i = 0; i < 5; i++) {
System.out.println("執行緒T2執行中: " + i);
if (i == 3) {
try {
T1.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
System.out.println("T2結束");
});
T1.start();
T2.start();
}
}
結果如下,不管怎麼測試,都會是T2最後執行結束。
Java的執行緒狀態
執行緒具有最基本的三態(就緒、執行、阻塞)。執行緒與程式一樣,各執行緒之間也存著共享資源和互相合作的制約關係,致使執行緒執行時具有間斷性。接下來看一下如圖,這是五種狀態的轉化。
執行緒的不同狀態
在Java執行緒中有6中狀態,從執行緒的建立到執行緒的終止。執行緒建立為NEW建立態,透過start啟動執行緒,執行緒內部會從就緒態轉成執行態,在Java執行緒中統稱為“執行態”,執行緒由於被掛起、呼叫yeild等方法能夠使執行緒從執行態轉成就緒態,也能夠透過執行緒的其他方法或者鎖阻塞執行緒,直到時間結束或者是獲得鎖等,從而回到RUNABLE狀態。
- 初始(NEW):新建立了一個執行緒物件,但還沒有呼叫start()方法。
- 執行(RUNNABLE):Java執行緒中將就緒(Ready)和執行中(Running)兩種狀態籠統的稱為“執行”。執行緒物件建立後,其他執行緒(比如main執行緒)呼叫了該物件的start()方法。該狀態的執行緒位於可執行執行緒池中,等待被執行緒排程選中,獲取CPU的使用權,此時處於就緒狀態(Ready)。就緒狀態的執行緒在獲得CPU時間片後變為執行中狀態(Running)。
- 阻塞(BLOCKED):表示執行緒阻塞於鎖。
- 等待(WAITING):進入該狀態的執行緒需要等待其他執行緒做出一些特定動作(通知或中斷)。
- 超時等待(TIMED_WAITING):該狀態不同於WAITING,它可以在指定的時間後自行返回。
- 終止(TERMINATED):表示該執行緒已經執行完畢。
Java執行緒的狀態轉化
如執行緒狀態轉換圖,以下就是Java執行緒狀態的轉換流程。執行緒可以透過實現Runnable介面或者繼承Threa類,然後去例項化Java的執行緒物件。線上程被執行之前都是屬於建立態(NEW),在呼叫start方法後,執行緒就會轉成RUNABLE狀態,在RUNABLE中,當執行緒處於就緒態(Ready)的時候,經過排程分配了cpu資源,這時轉成了執行態,當執行緒被掛起、執行緒執行了yield,執行緒將會退回就緒態。在執行態(RUNABLE),也會透過一些處理而被阻塞或者等待。終止狀態(TERMINATED)是執行緒執行完畢退出,此時,終止態的執行緒不會直接轉成建立態。
Java執行緒狀態程式碼
Java中執行緒的狀態都是在java.lang.Thread.State的列舉類中。
可以看一下以下列舉程式碼,分別為(NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED)六種。
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
接下來看一下演示程式碼
public class T6_Thread_State {
static class Thread1 extends Thread {
@Override
public void run() {
System.out.println("run - 當前執行緒的狀態: " + this.getState());
for (int i = 0; i < 5; i++) {
try {
System.out.println("sleep前 - 當前執行緒的狀態: " + this.getState());
Thread.sleep(1000);
System.out.println("sleep後 - 當前執行緒的狀態: " + this.getState());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread1 t1 = new Thread1();
System.out.println("main - 當前執行緒的狀態: " + t1.getState());
t1.start();
System.out.println("join前 - 當前執行緒的狀態: " + t1.getState());
t1.join();
System.out.println("join後 - 當前執行緒的狀態: " + t1.getState());
}
}
執行結果,可以只管看到執行緒在執行過程種的狀態切換。
博文推薦
這裡推薦給各位一篇很不錯的部落格文章,是針對Java執行緒的狀態轉換的詳細介紹
?創作不易,如有錯誤請指正,感謝觀看!記得點贊哦!?