JavaEE初階第三期:解鎖多線程,從 “單車道” 到 “高速公路” 的編程升級(一)

專欄:JavaEE初階起飛計劃

個人主頁:手握風云

目錄

一、認識線程

1.1. 概念

1.2. 為什么要使用線程

1.3. 進程和線程的關系

1.4. 多線程模型

二、多線程的創建

2.1. 繼承Thread類

2.2. 實現Runnable接口

2.3. 匿名內部類

2.4. lambda表達式


一、認識線程

1.1. 概念

????????進程是操作系統進行資源分配和調度的基本單位。簡單來說,進程就是程序的一次執行過程。通過進程,用戶可以同時運行多個應用程序,提高計算機的利用率和用戶體驗。同時,進程間的隔離性也增強了系統的穩定性和安全性,一個進程的崩潰通常不會影響到其他進程。

1.2. 為什么要使用線程

? ? ? ? 進程雖然說可以實現并發編程的效果,比如寫一個服務器來同時處理多個進程,可以進程級別太高,創建或者銷毀進程的開銷很大。為了優化這些問題,于是引入了線程。

1.3. 進程和線程的關系

? ? ? ? 進程包含線程,線程也可以被稱為輕量級進程。每個線程都可以獨立執行一段邏輯,并且獨立在CPU上調度;同一個進程中的多個線程,共享進程的資源,如內存資源、文件描述符表等。當進程已經有了,在進程內部在創建新線程,就把申請資源開銷省下來了。

1.4. 多線程模型

????????我們假設有一個滑稽老鐵去吃100只燒雞,花費的時間肯定會太長。按照多進程模型,讓兩個滑稽老鐵一人分別吃50只,雖然效率提升了,但我們需要額外申請內存空間。

? ? ? ? 但我們如果按照多線程的模型來,讓兩個滑稽老鐵在同一個桌子上來分別吃50只燒雞,省下了申請資源的開銷,效率就會進一步提高。

? ? ? ? 但線程并不是越多,效率就越高。當滑稽老鐵的數目進一步增多,但桌子就這么大,有可能其中一人吃得好好的,會被其他人擠走。此時非但不會提高效率,效率反而越低了,那此時就只能對機器進行性能優化。

? ? ? ? 當線程數目比較少時,也會出現一些問題:

  • 當兩個線程嘗試操作一個共享的資源時,比如內存中的同一個變量,就可能會發生沖突,從而引起bug。
  • 如果某個線程拋出異常,且沒有其他代碼把這個異常catch住,就會導致進程內的所有線程會被隨之帶走,整個進程也會結束。

二、多線程的創建

????????多線程編程和多進程編程都是操作系統提供的API,因為Java是一次編譯到處運行的,JVM已經把系統差異給屏蔽了,JVM已經把多線程的API進行了較好地封裝,但多進程的API封裝得比較粗糙。下面就通過代碼來感受一下Java中如何使用多線程。

2.1. 繼承Thread類

public class Demo1 {
// 定義一個靜態內部類MyThread,繼承自Thread類static class MyThread extends Thread {// 重寫run方法@Overridepublic void run() {System.out.println("hello thread");}}public static void main(String[] args) {// 創建一個MyThread對象Thread thread = new MyThread();// 啟動線程thread.start();}
}

????????Thread類是Java中的核心類,用于表示和管理線程。由于操作系統本身就提供了一組操作線程的函數,但這個函數是C語言版本的。JVM把這些操作系統的函數給封裝成了Java版本,到了程序員手中,就成了Thread。使用Thread類,就相當于在使用操作系統的API。run方法是線程執行體,包含了線程需要執行的任務代碼。當線程啟動時,run方法中的代碼將在新的線程中執行,也就是多線程程序的入口。

? ? ? ? 在MyThread類里面重寫的是run()方法,但在main()方法里調用的卻是start()方法,這是因為start()方法是調用系統API,真正在操作系統內部創建一個線程,這個新的線程就會以run()作為入口,執行里面的邏輯,而run()方法就不需要在代碼中顯式調用。

? ? ? ? 但如果我們調用run()方法,雖然也能打印"hello thread",但沒有創建新的線程,而是直接在main()方法所在的 “主線程” 里執行了run()方法的邏輯。對于一個進程,至少得有一個線程;對于一個Java程序來說,main()方法,所在的進程至少會有這個線程,這個線程就是主線程。上面的代碼就具有一個主線程和一個新創建的線程。

  • sleep靜態方法

? ? ? ? Thread.sleep是Thread類中的一個靜態方法。當調用這個方法時,當前線程會被阻塞,不執行任何操作,直到指定的毫秒數過去。這個方法可以用于多種場景,比如在循環中添加延遲,或者在執行某些操作之前等待一段時間。

public class MyThread extends Thread {@Overridepublic void run() {System.out.println("Hello Thread!");}
}
public class Demo2 {public static void main(String[] args) {// 創建一個MyThread對象Thread thread = new MyThread();// 啟動線程thread.start();while (true) {System.out.println("Hello Main!");// 休眠1000秒Thread.sleep(1000);}}
}

? ? ? ? 但此時的代碼會顯示出錯誤,原因是我們有受查的異常,必須進行顯式處理,可以用throws拋出或者try{}catch{}進行捕獲。

//throws
public class Demo2 {public static void main(String[] args) throws InterruptedException {// 創建一個MyThread對象Thread thread = new MyThread();// 啟動線程thread.start();while (true) {System.out.println("Hello Main!");// 休眠1000秒Thread.sleep(1000);}}
}//try{}catch{}
public class Demo2 {public static void main(String[] args) {// 創建一個MyThread對象Thread thread = new MyThread();// 啟動線程thread.start();while (true) {System.out.println("Hello Main!");// 休眠1000秒try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

? ? ? ? 同理,我們在MyTread類里面進行循環打印。但這里只能使用try{}catch{}進行捕獲,因為run()方法是重寫自父類的方法,如果使用throws拋出不符合父類方法的聲明。

? ? ? ? 完整代碼實現:

public class MyThread extends Thread {@Overridepublic void run() {while (true) {System.out.println("Hello Thread!");try {// 休眠1秒Thread.sleep(1000);} catch (InterruptedException e) {// 拋出運行時異常throw new RuntimeException(e);}}}
}
public class Demo2 {public static void main(String[] args) throws InterruptedException {// 創建一個MyThread對象Thread thread = new MyThread();// 啟動線程thread.start();while (true) {System.out.println("Hello Main!");// 休眠1000秒Thread.sleep(1000);}}
}

? ? ? ? 運行結果如下,雖然兩個打印分別在不同的while循環中,兩個線程屬于并發關系,獨立在CPU上執行。我們同時也可以看到兩個線程不是嚴格交替執行的,由于兩個線程都加了休眠,當1000毫秒到后,哪個線程線程的執行順序是無法確定的,這是因為操作系統調度的線程是隨機不可預測的。

? ? ? ? 我們還有更直觀的辦法來觀察多線程:在我們安裝的JDK里面的bin目錄下,以管理員身份運行jconsole.exe,然后點擊連接本地進程Demo2,然后點擊線程,就可以看到我們的Thread-0和main兩個線程,剩下的線程都是JVM自帶的,這些線程進行了一些背后的操作,比如垃圾回收、記錄統計信息、記錄一些調試信息等。

2.2. 實現Runnable接口

public class Demo3 {public static void main(String[] args) throws InterruptedException {// 創建一個實現了Runnable接口的實例Runnable myRunnable = new MyRunnable();// Runnable本身沒有start方法,需要配合Thread類來使用// 創建一個Thread對象,并將myRunnable作為參數傳入Thread thread = new Thread(myRunnable);// 啟動線程thread.start();while (true) {System.out.println("Hello Main!");Thread.sleep(1000);}}
}class MyRunnable implements Runnable {@Overridepublic void run() {while (true) {System.out.println("Hello Tread!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

????????運行結果:

? ? ? ? 對于第一種繼承Thread寫法,描述任務的時候,代碼是寫到Thread子類中的,意味著任務內容與Thread類耦合度高,未來想把這個任務給別的主體來執行。

? ? ? ? 對于第二種Runnable接口寫法,任務是寫到Runnable中,不涉及到任何和“線程”的概念,任務內容和Thread耦合度幾乎沒有,后序可以把這個任務交給進程或者協程來執行。

2.3. 匿名內部類

public class Demo4 {public static void main(String[] args) {// 創建一個新的線程Thread thread = new Thread() {@Overridepublic void run() {while (true) {System.out.println("Hello Tread!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};// 啟動線程thread.start();while (true) {// 在主線程中輸出"Hello Main!"System.out.println("Hello Main!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

? ? ? ? 此處創建了沒有的名字匿名內部類,并且這個類是Thread類的子類,該子類也重寫了run()方法,也創建了子類實例,通過thread引用指向子類。這樣寫可以簡化代碼。

? ? ? ? 運行結果如下:

2.4. lambda表達式

public class Demo5 {public static void main(String[] args) {// 創建一個新的線程Thread thread = new Thread(() -> {while (true) {System.out.println("Hello Thread!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});// 啟動線程thread.start();// 無限循環while (true) {System.out.println("Hello Main!");try {// 主線程休眠1秒Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

? ? ? ? 運行結果如下:

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/910374.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/910374.shtml
英文地址,請注明出處:http://en.pswp.cn/news/910374.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【StarRocks系列】建表優化

目錄 一、數據模型選擇 (核心優化) 二、分區與分桶策略 (數據分布優化) 三、字段類型與壓縮 四、索引策略 五、高級特性應用 六、建表示例(關鍵優化整合) 參考官網 優化性能 | StarRocks 在 StarRocks 中創建表時,合理的表設計是性能優…

linux-vim編輯器

linux-vim編輯器 前言一、命令模式1. 跳轉功能2. 文本編輯3. 模式切換 二、輸入模式1. 進入輸入模式2. 快捷鍵 三、末行模式1. 進入末行模式2. 文件操作3. 查找與替換4. 行操作 四、替換模式五、可視模式1. 進入可視模式2. 文本操作 六、相關配置 前言 vim - Vi IMproved, a p…

SQL關鍵字三分鐘入門: 表結構管理與分區設計。(ALTER、MODIFY、CHANGE、DEFAULT、VALUES、LESS THAN、RANGE)

前面我們已經學習了如何查詢數據(SELECT)、篩選數據(WHERE)等操作。現在我們要進入數據庫的另一個重要領域 —— 表結構管理與分區設計。 本文帶你快速認識以下關鍵字: ? ALTER? MODIFY? CHANGE? DEFAULT? VALU…

深度剖析:RTTI輕量框架實現原理與架構(C++ 17 高級編程)

🚀 C RTTI反射系統深度設計文檔 🌌 核心架構圖 #mermaid-svg-aWkaWoFklq1ylap6 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-aWkaWoFklq1ylap6 .error-icon{fill:#552222;}#mermaid-svg-a…

03-D3.js SVG text標簽?

Data Visualization D3.js ? SuperHiLearn how to create interactive, engaging experiences using HTML, CSS, SVG and Javascript.https://www.superhi.com/catalog/data-visualization-with-d3 text - SVG:可縮放矢量圖形 | MDNtext元素定義了一個由文字組成…

Python 使用Gitlab Api

代碼 REST API 見自帶幫助文檔 python 安裝python-gitlab pip install --upgrade python-gitlab使用API 參考:https://python-gitlab.readthedocs.io/en/stable/api-usage.html import gitlab# anonymous read-only access for public resources (GitLab.com…

中醫體質識別:理論、方法與應用的簡要綜述

中醫體質識別:理論、方法與應用的簡要綜述 摘要 中醫體質識別是中醫“治未病”及個性化診療的關鍵環節。本文系統闡述中醫體質識別,涵蓋理論基礎、常見體質類型、識別方法、現代技術應用及臨床實踐。中醫體質理論源遠流長,《黃帝內經》奠定…

稀疏表原理及應用場景

1 概述 稀疏表(Sparse Table,ST)是一種用于高效解決 靜態區間查詢(Range Query) 問題的數據結構,主要用于 可重復貢獻問題(Idempotent Range Queries),例如區間最小值&a…

【深度學習與機器學習的區別】從本質到應用的全景對比

目錄 前言 一、三者關系:深度學習是機器學習的子集 1.1 概念關系 1.2 類比理解:動物 vs 哺乳動物 1.3 舉個例子更清楚 1.4 為什么“機器學習 ≠ 深度學習”? 1.5 最容易搞混的地方 二、核心區別總覽(對比表) …

Masscan常用命令詳解

一、工具介紹 Masscan是一款開源、高速的網絡端口掃描工具,設計目標是實現最快的掃描速度。它能夠在極短的時間內完成大規模的網絡掃描,適用于互聯網級別的掃描任務。它采用異步傳輸和自定義TCP/IP協議棧技術,最快可實現每秒160萬數據包的掃…

STM32的內部RC與外部晶振電路

內部RC是“能用”,外部晶振是“用得準”。 一、STM32芯片內部的“晶振電路”是什么? STM32內部確實集成了兩個RC(電阻-電容)振蕩器: HSI(高速內部振蕩器):通常8MHz,精…

為OneCode 開發TRea 開發插件,從環境搭建到生態融合

作為 AI 原生開發環境,TRea 的插件體系支持開發者基于其核心能力(如自然語言代碼生成、AI 代碼分析)進行功能擴展。本文以開發一個 "OneCode 組件生成插件" 為例,詳解如何通過 TRea 開放接口實現自定義功能,…

Spring JDBC配置與講解

目錄 一、Spring JDBC概述1、Spring JDBC需要配置的依賴2、Spring配置項文件配置 二、Spring JDBC的使用1、Spring JDBC的增加操作2、Spring JDBC的修改操作3、Spring JDBC的刪除操作4、Spring JDBC的查詢操作 三、Spring JDBC的事務1、xml的形式進行事務2、Transactional注解 …

【AI智能體】Spring AI MCP 服務常用開發模式實戰詳解

目錄 一、前言 二、MCP 介紹 2.1 MCP是什么 2.2 MCP 核心特點 2.3 Spring AI MCP 介紹 2.3.1 Spring AI MCP架構 2.3.2 Spring AI MCP分層說明 2.4 兩種模式介紹 三、本地開發SSE模式 3.1 搭建mcp-server 3.1.1 導入工程核心依賴 3.1.2 添加配置文件 3.1.3 提供兩個…

OpenStack 入門與實踐

一、云計算概述 1.1 云計算的定義與本質 云計算(Cloud Computing)是一種基于網絡的超級計算模式,它能夠根據用戶的不同需求,動態提供所需的計算資源、存儲資源和網絡資源等。這種模式就像我們日常生活中使用水電煤氣一樣&#x…

AntV L7入門教程

以下教程將系統地介紹 AntV?L7 的核心 Scene 類用法,涵蓋實例化、地圖配置、視圖操作、圖層管理、事件監聽及資源銷毀等常用 API,并為每個方法給出完整示例代碼。所有示例均基于官方 API 文檔 ([l7.antv.antgroup.com][1])。 一、安裝與引入 # 安裝 L7…

【邊緣計算】場景

工業互聯網 對現場采集的數據進行數據預處理,將現場有用的信息提取出來實時上傳給平臺,為平臺大大減輕了處理的工作量。 匯聚現場數據統一接口上傳數據到云端,大大提高系統多樣部署的安全性,解決現場數據跨域訪問的問題。制造企業…

【FPGA學習】DDS信號發生器設計

目錄 一、設計原理與準備? 1.1 DDS 原理? 1.2 IP 核學習與準備?:FPGA開發中常用IP核——ROM/RAM/FIFO 2、ROM文件的設置 1.3 開發環境搭建? 二、DDS 信號發生器設計實現 2.1 系統架構設計? 2.2 代碼編寫與模塊實現? 三、測試結果與總結? 參考文獻&…

pyqt 簡單條碼系統

生產數據管理系統說明 系統概述 這是一個基于PyQt5和pyodbc開發的生產數據管理系統,主要用于管理生產過程中的物料綁定和查詢操作。系統提供了上料綁定和下料查詢功能,支持與SQL Server數據庫交互,實現數據的插入、查詢、更新和刪除操作。界…

【unitrix】 4.1 類型級加一操作(Add1.rs)

一、原碼 這段代碼實現了一個類型級的加一操作(Add1 trait),用于在Rust的類型系統中進行數值加一運算。 //! 加一操作特質實現 / Increment operation trait implementation //! //! 說明: //! 1. Z0、P1,、N1 1,常規計算 //! 2. …