# 牛年自強計畫 Week 1 - SpringBoot - 番外篇 Java 執行緒 Thread/Runnable/Callable ### 【前言】 這次花點時間寫關於執行緒相關的文章,因為個人這部分一直理解得不是很好,也趁鐵人賽剛結束時候給他惡補一下。 ### 【介紹 Thread】 執行緒 (Thread),在部分文章中又稱為線程,其實是同一件事情。在介紹執行緒之前,首先要了解程式(Program)、程序(Process)、執行緒(Thread) 是什麼: - **程式 (Program)** 包裝邏輯與相關程式碼成一個可以執行的程式包,諸如 .exe, .jar, .war 等等。 - **程序 (Process)** OS 給予執行程式的資源與空間的排程單位,一個程式的運行會伴隨一個系統程序的創建,當程式運行結束,則程序會被 OS 自動刪除。因此對比靜態的程式,程序是有其**生命周期**的。 - **執行緒 (Thread)** 程序中更小的執行實體,負責運行程式內部程式碼的邏輯,當程式有需要增加執行實體,便會要求更多目前系統的資源去建立新的。  > 執行同樣的程式 2 次,會有 2 個 Process 執行個別的程式 > 執行的程式若有建立 2 個或多個執行緒的邏輯,則會在主流程(Thread_1)中切出一條新的(Thread_2) 比較了解執行緒是什麼之後,接著講在 Java 中如何實現建立新的執行緒的方式,這就會介紹到本篇的主角 Thread 和 Runnable 了 - ### Runnable Interface 為 Interface 類別,是編寫執行緒 **邏輯** 的最簡單的類,所有需要開出新執行緒的邏輯程式碼都必須寫在實現 Runnable 的 run() 中。 上述實現 Runnable 的類別都需要被放入一個 Thread 的實體中運行。 - ### Thread Class 實現 Runnable 的基本執行緒類別,除了實現 Runnable 類的 run() 方法外,還增加了許多真正建立、啟動、暫停、禮讓、續行、刪除等操作方法。 可以說是一個執行緒最基本的實現類別。 - ### Callable Interface 不論 Runnable 或是 Thread 都有一個缺點,那就是當任務完成後無法回傳結果。 Callable 就是在這種需求下被設計出來的,配和後續會提到的 Future 和 FutureTask 類,實現了取得異步任務結果的方法。 ### 【懶人包】 :::success 若把 Runnable 比喻為一份企劃,所有實現 Runnable 的類就是被寫成的不同的企劃。 Thread 更像是執行企劃的人,他可以自行編寫新的企劃、執行其他企劃、棄用企劃等動作。 ::: ### 【單執行緒/多執行緒】 每一支程式至少一定有一條執行緒去執行程式邏輯,稱作**主執行緒** 而因為這類只有單條執行緒便可執行完成的也稱作**單執行緒程式** 多執行緒顧名思義就是單一程式內,因應程式碼建立了多個子執行緒而被稱作**多執行緒程式** 主執行緒和子執行緒並無歸屬關係,但主執行緒一但結束,則在主執行緒執行期間開出的子執行緒會被強迫結束! ### 【範例】 說了上述這麼多,來個範例了解一下便可,實際上執行緒的使用還需要慎重搭配業務邏輯。 - #### build.gradle ```gradle= plugins { id 'org.springframework.boot' version '2.3.3.RELEASE' id 'io.spring.dependency-management' version '1.0.8.RELEASE' id 'java' } group 'org.example' version '1.0-SNAPSHOT' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.junit.jupiter:junit-jupiter-api:5.6.0' implementation 'org.junit.jupiter:junit-jupiter-engine:5.6.0' } test { useJUnitPlatform() } ``` - #### testRunnable.java ```java= package Thread.Threads; public class testRunnable implements Runnable { @Override public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Runnable sleep for 5 seconds."); } } ``` - #### testThread.java ```java= package Thread.Threads; public class testThread extends Thread{ @Override public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread sleep for 5 seconds."); } } ``` - #### test.java ```java= package Thread.Threads; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; public class test { @DisplayName("Test Thread and Runnable") @Test public void testCase1() { new Thread(new testRunnable()).start(); new testThread().start(); int i = 0; while (i < 10){ System.out.println("Now is " + i + " second."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } i += 1; } } @BeforeAll public static void BeforeAll(){ System.out.println("Test Start"); } @AfterAll public static void AfterAll(){ System.out.println("Test End"); } } ``` #### 【範例解說】 Kai 這邊設計了兩個執行緒,分別實做 Runnable 和 Thread 方式,並放入 test.java 中執行測試 可以清楚看到兩者方法內都是需要將程式邏輯寫入 run() 中,如果有興趣點開 Thread 這個物件的話會清楚看到其實做了 Runnable Interface。 再次提醒 Runnable 必須放入一個 Thread 的物件中,透過 Thread 啟動才能夠被執行! :::warning ※ 小提醒 若執行緒尚未完成時候,主程序已經執行完畢了,則會強制停止執行緒的執行,因此 Kai 在程式中才會設計一道迴圈慢慢等完成。 實際在職場上的用法來說,執行緒多半都會在最後做一個收合,然現今強調 MicroServices 的架構下可能不會需要工程師自己建構多執行緒的部分了。 後期可能在找機會寫一篇關於 **執行緒 & Multi-Request(C10K,C100K等) & Redis & MQ** 在實務面上的用法 ::: 下一篇: [牛年自強計畫 Week 2 - SpringBoot - 番外篇 Java 執行緒 Thread Pool](/W1zAA_sWSqWkIw2dkdPkzQ) 首頁 [Kai 個人技術 Hackmd](/2G-RoB0QTrKzkftH2uLueA) ###### tags: `Spring Boot`
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up