Java多線程編程:實現并發處理的高效利器

Java多線程編程:實現并發處理的高效利器

作者:Stevedash

發表于:2023年8月13日 20點45分

來源:Java 多線程編程 | 菜鳥教程 (runoob.com)

? 在計算機領域,多線程編程是一項重要的技術,可以使程序同時執行多個任務,充分利用計算資源,提高系統的性能和響應能力。Java作為一門廣泛應用的編程語言,提供了豐富的多線程編程支持,使得開發人員可以輕松實現并發處理。本篇博客將向您介紹Java多線程編程的基本概念、創建線程的方式、線程同步和線程通信等內容,同時還會涉及到線程的生命周期、線程的優先級和線程的常用方法。

多線程編程的優勢

? 多線程編程在提高程序性能、資源利用率和響應速度方面具有明顯的優勢。通過充分利用多核處理器,可以在同一時刻處理多個任務,提高系統的整體吞吐量。此外,多線程還可以用于實現一些需要并發執行的場景,如并發請求處理、數據采集、實時計算等。

創建線程的方式

Java多線程編程有三種常見的方式來創建線程:

  1. 繼承Thread類: 創建一個類繼承Thread類,并重寫run()方法來定義線程要執行的任務。

  2. 實現Runnable接口: 創建一個實現Runnable接口的類,并實現run()方法,然后通過Thread類的構造方法創建線程對象。

  3. 實現Callable接口: 創建一個實現Callable接口的類,并實現call()方法,可以獲取線程執行后的返回值。

? 每種方式都有其特點,選擇合適的方式取決于具體需求。使用繼承Thread類的方式編寫簡單,但由于Java不支持多重繼承,可能限制了其他類的繼承。而實現Runnable或Callable接口的方式可以更靈活地管理線程。


線程的生命周期

線程是一個動態執行的過程,它也有一個從產生到死亡的過程。

下圖顯示了一個線程完整的生命周期。img

線程的生命周期可以分為以下幾個階段:

  1. 新建狀態(New): 使用 new 關鍵字和 Thread 類或其子類建立一個線程對象后,該線程對象就處于新建狀態。它保持這個狀態直到程序 start() 這個線程。
  2. 就緒狀態(Runnable): 當調用線程的start()方法后,線程進入就緒狀態,就緒狀態的線程處于就緒隊列中,要等待JVM里線程調度器的調度,等待獲取CPU時間片執行。
  3. 運行狀態(Running): 如果就緒狀態的線程獲取 CPU 資源,就可以執行 run(),此時線程便處于運行狀態。處于運行狀態的線程最為復雜,它可以變為阻塞狀態、就緒狀態和死亡狀態。
  4. 阻塞狀態(Blocked): **如果一個線程執行了sleep(睡眠)、suspend(掛起)等方法,失去所占用資源之后,該線程就從運行狀態進入阻塞狀態。**在睡眠時間已到或獲得設備資源后可以重新進入就緒狀態。可以分為三種:
    • 等待阻塞:運行狀態中的線程執行 wait() 方法,使線程進入到等待阻塞狀態。
    • 同步阻塞:線程在獲取 synchronized 同步鎖失敗(因為同步鎖被其他線程占用)。
    • 其他阻塞:通過調用線程的 sleep() 或 join() 發出了 I/O 請求時,線程就會進入到阻塞狀態。當sleep() 狀態超時,join() 等待線程終止或超時,或者 I/O 處理完畢,線程重新轉入就緒狀態。
  5. 死亡狀態:(Terminated): 一個運行狀態的線程完成任務或者其他終止條件發生時,又或者當線程的run()方法執行完畢,線程進入終止狀態。

線程的優先級

每個線程都有一個優先級,用于指示線程在競爭CPU時間片時的優先順序。線程的優先級分為1到10,其中1為最低優先級,10為最高優先級。可以使用setPriority()方法設置線程的優先級,但并不能保證優先級高的線程一定會先執行。**默認情況下,每一個線程都會分配一個優先級 NORM_PRIORITY(5)。**具有較高優先級的線程對程序更重要,并且應該在低優先級的線程之前分配處理器資源。但是,線程優先級不能保證線程執行的順序,而且非常依賴于平臺。


線程的常用方法

Java提供了一些常用的線程方法,用于管理線程的執行和狀態:
  • start(): 啟動線程,使其進入就緒狀態。

  • join(): 等待線程執行完畢。

  • sleep(): 使線程休眠一段時間。

  • yield(): 讓出CPU時間片,讓其他線程執行。

  • isAlive(): 判斷線程是否還存活。

示例代碼

以下是一個簡單的Java程序,演示了如何創建并啟動多個線程,并使用線程的優先級和常用方法:
public class MultiThreadDemo {public static void main(String[] args) {Thread thread1 = new Thread(new MyRunnable("Thread 1"));Thread thread2 = new Thread(new MyRunnable("Thread 2"));thread1.setPriority(Thread.MAX_PRIORITY);thread2.setPriority(Thread.MIN_PRIORITY);thread1.start();thread2.start();try {thread1.join(); // 等待thread1執行完畢thread2.join(); // 等待thread2執行完畢} catch (InterruptedException e) {e.printStackTrace();}System.out.println("主線程結束");}//實現Runnable接口創建線程static class MyRunnable implements Runnable {private String name;public MyRunnable(String name) {this.name = name;}//通過Callable接口和Future創建線程static class MyCallable implements Callable<Integer> {private String name;public MyCallable(String name) {this.name = name;}@Overridepublic Integer call() throws Exception{//...return Integer;}//繼承Thread類創建線程  三選一static class MyThread extends Thread{private String name;public MyCallable(String name) {this.name = name;}}//必須要重寫run方法@Overridepublic void run() {for (int i = 1; i <= 5; i++) {System.out.println(name + ": " + i);try {Thread.sleep(1000); // 休眠1秒} catch (InterruptedException e) {e.printStackTrace();}}}}
}

下面是Thread 方法

下表列出了Thread類的一些重要方法:

序號方法描述
1public void start() 使該線程開始執行;Java 虛擬機調用該線程的 run 方法。
2public void run() 如果該線程是使用獨立的 Runnable 運行對象構造的,則調用該 Runnable 對象的 run 方法;否則,該方法不執行任何操作并返回。
3public final void setName(String name) 改變線程名稱,使之與參數 name 相同。
4public final void setPriority(int priority) 更改線程的優先級。
5public final void setDaemon(boolean on) 將該線程標記為守護線程或用戶線程。
6public final void join(long millisec) 等待該線程終止的時間最長為 millis 毫秒。
7public void interrupt() 中斷線程。
8public final boolean isAlive() 測試線程是否處于活動狀態。
上述方法是被 Thread 對象調用的,下面表格的方法是 Thread 類的靜態方法。
序號方法描述
1public static void yield() 暫停當前正在執行的線程對象,并執行其他線程。
2public static void sleep(long millisec) 在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行),此操作受到系統計時器和調度程序精度和準確性的影響。
3public static boolean holdsLock(Object x) 當且僅當當前線程在指定的對象上保持監視器鎖時,才返回 true。
4public static Thread currentThread() 返回對當前正在執行的線程對象的引用。
5public static void dumpStack() 將當前線程的堆棧跟蹤打印至標準錯誤流。

重點了解一下SetDeamon()標記成守護線程或者用戶線程

基本概念:

? 守護線程(Daemon Thread)是一種在后臺運行的線程,它的存在不會阻止程序的終止。與之相對,用戶線程(User Thread)是主要用于執行應用程序邏輯的線程,“如果所有的用戶線程都執行完畢,JVM 就會退出,而不管守護線程是否還在運行。”

? 在 Java 中,我們可以通過 setDaemon(true) 方法將一個線程設置為守護線程。**默認情況下,線程都是用戶線程,不會影響程序的終止。**如果想要使用守護線程,可以在創建線程后調用 setDaemon(true) 來設置它為守護線程。


守護線程的主要作用有以下幾點:

  1. 后臺任務執行: 守護線程通常用于執行一些不需要持續運行的后臺任務,如垃圾回收(Garbage Collection)、內存管理等。通過將這些任務放在守護線程中,可以在主要任務執行完畢后,自動進行清理等工作,提高系統的整體性能和資源利用率。
  2. 資源管理: 守護線程可以用于監控和管理一些資源,如數據庫連接池、網絡連接等當用戶線程都結束時,守護線程可以負責關閉這些資源,防止資源泄露和浪費。
  3. 周期性任務: 守護線程還可以用于執行周期性的任務,如定時器任務。這些任務可以在“后臺周期性地執行,而不影響主要業務邏輯的進行。”

? PS(這很重要基本上就是守護線程的重點):守護線程在程序終止時會突然中斷,因此不應該在它們的代碼中進行需要確保完整性的操作。另外,守護線程不應該依賴于特定的執行順序,因為它們的執行可能會受到系統資源調度的影響。


三種創建線程的方式對比

  1. 繼承 Thread 類:

    • 創建線程的方式之一是繼承 Thread 類,并重寫 run 方法來定義線程的任務邏輯。
    • 優點:簡單,不需要額外的類來實現接口。
    • 缺點:由于 Java 不支持多繼承,因此如果已經繼承了其他類,則無法再使用這種方式創建線程。
    • 示例代碼:
    class MyThread extends Thread {public void run() {// 線程執行的任務邏輯}
    }public class ThreadExample {public static void main(String[] args) {MyThread thread = new MyThread();thread.start();}
    }
    
  2. 實現 Runnable 接口:

    • 另一種創建線程的方式是實現 Runnable 接口,并將實現類的實例傳遞給 Thread 類的構造函數。
    • 優點:可以避免繼承單一父類的限制,同時更靈活,可以實現多個接口。
    • 缺點:相對于繼承 Thread 類,需要額外的類來實現接口。
    • 示例代碼:
    class MyRunnable implements Runnable {public void run() {// 線程執行的任務邏輯}
    }public class RunnableExample {public static void main(String[] args) {Thread thread = new Thread(new MyRunnable());thread.start();}
    }
    
  3. 使用 CallableFuture

    • 使用 Callable 接口可以創建一個帶有返回結果的任務。Future 接口可以用于獲取任務的執行結果。
    • 優點:可以獲取任務的返回結果,能夠捕獲任務拋出的異常。
    • 示例代碼:
    import java.util.concurrent.*;class MyCallable implements Callable<Integer> {public Integer call() throws Exception {// 線程執行的任務邏輯return 42;}
    }public class CallableExample {public static void main(String[] args) {ExecutorService executor = Executors.newSingleThreadExecutor();Future<Integer> future = executor.submit(new MyCallable());try {int result = future.get();System.out.println("任務執行結果:" + result);} catch (Exception e) {e.printStackTrace();} finally {executor.shutdown();}}
    }
    

三種創建線程方式對比小結:

  1. 采用實現 Runnable、Callable 接口的方式創建多線程時,線程類只是實現了 Runnable 接口或 Callable 接口,還可以繼承其他類。
  2. 使用繼承 Thread 類的方式創建多線程時,編寫簡單,如果需要訪問當前線程,則無需使用 Thread.currentThread() 方法,直接使用 this 即可獲得當前線程。
  3. 繼承 Thread 類和實現 Runnable 接口是最基本的方式,而使用 CallableFuture 可以獲得更多的控制和返回結果的能力。

線程的幾個主要概念

在多線程編程時,你需要了解以下幾個概念:

  • 線程同步
  • 線程間通信
  • 線程死鎖
  • 線程控制:掛起、停止和恢復

注意事項和進階功能

  • 多線程編程需要注意線程安全性和資源競爭問題,合理使用同步機制來保證數據的一致性。

  • Java提供了線程池、并發集合等工具類來簡化多線程編程,提高效率和性能。

  • 在處理復雜場景時,可以使用Executor框架、Future接口等實現更高級的線程管理和任務調度。


總結

? Java多線程編程是實現并發處理的有效手段,可以提高程序性能和響應能力。通過學習線程創建方式、線程同步、線程通信、線程的生命周期、線程的優先級和線程的常用方法,我們可以在應用程序中合理使用多線程來實現并發任務。

作者:Stevedash

發表于:2023年8月13日 20點45分

來源:Java 多線程編程 | 菜鳥教程 (runoob.com)

注:本文內容基于個人學習理解,如有錯誤或疏漏,歡迎指正。感謝閱讀!如果覺得有幫助,請點贊和分享。

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

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

相關文章

InnoDB文件物理結構解析6 - FIL_PAGE_INDEX

本文討論Secondary Key Page的解析&#xff0c;也就是表非主鍵索引的記錄存儲。與Clustered Key Page有相同的基本記錄結構&#xff0c;也細分為Leaf Page和Non-Leaf Page&#xff0c;我們先看結構&#xff1a; ### Contents (Secondary Key - Leaf Page) ### ---------------…

從小白到大神之路之學習運維第79天-------Kubernetes網絡組件詳解

第四階段 時 間&#xff1a;2023年8月14日 參加人&#xff1a;全班人員 內 容&#xff1a; Kubernetes網絡組件詳解 目錄 一、Kubernetes網絡組件 &#xff08;一&#xff09;Flannel網絡組件 &#xff08;二&#xff09;Calico 網絡插件 &#xff08;1&#xff09;…

設計模式——建造者(Builder)模式

建造者模式&#xff08;Builder Pattern&#xff09;&#xff0c;又叫生成器模式&#xff0c;是一種對象構建模式 它可以將復雜對象的建造過程抽象出來&#xff0c;使這個抽象過程的不同實現方法可以構造出不同表現的對象。建造者模式是一步一步創建一個復雜的對象&#xff0c;…

(14)嵌套列表,Xpath路徑表達式,XML增刪查改,Implicit,Operator,Xml序列化,淺拷貝與深拷貝

一、作業問題 1、問&#xff1a;listbox1.items[i]返回的object是指的字符串嗎&#xff1f; 答&#xff1a;items是真正的對象集合&#xff0c;在Add時加的是Person對象p&#xff0c;則里面的item就是Person對象p。 但是&#xff0c;在listbox1顯…

在單元測試中使用Jest模擬VS Code extension API

對VS Code extension進行單元測試時通常會遇到一個問題&#xff0c;代碼中所使用的VS Code編輯器的功能都依賴于vscode庫&#xff0c;但是我們在單元測試中并沒有添加對vscode庫的依賴&#xff0c;所以導致運行單元測試時出錯。由于vscode庫是作為第三方依賴被引入到我們的VS C…

[oneAPI] BERT

[oneAPI] BERT BERT訓練過程Masked Language Model&#xff08;MLM&#xff09;Next Sentence Prediction&#xff08;NSP&#xff09;微調 總結基于oneAPI代碼 比賽&#xff1a;https://marketing.csdn.net/p/f3e44fbfe46c465f4d9d6c23e38e0517 Intel DevCloud for oneAPI&…

JVM 中的編譯器

在Java的世界里,JVM(Java Virtual Machine)扮演了重要的角色。JVM是一個虛擬機,是Java程序的運行環境,它能夠將Java字節碼文件解釋執行,使得Java程序可以跨平臺。在JVM內部,有一個重要的組件就是編譯器,它的作用就是將Java源代碼編譯成字節碼,讓JVM可以識別并執行。 …

redis集群和分片-Redis Cluster:分布式環境中的數據分片、主從復制和 Sentinel 哨兵

當涉及到 Redis 中的集群、分片、主從復制和 Sentinel 哨兵時&#xff0c;這些是構建分布式 Redis 環境中非常重要的概念和組件。下面詳細介紹這些概念以及它們在分布式環境中的作用。 Redis Cluster Redis Cluster 是 Redis 官方提供的分布式解決方案&#xff0c;用于管理和…

React源碼解析18(4)------ completeWork的工作流程【mount】

摘要 經過上一章&#xff0c;我們得到的FilberNode已經具有了child和return屬性。一顆Filber樹的結構已經展現出來了。 那我們最終是想在頁面渲染真實的DOM。所以我們現在要在completeWork里&#xff0c;構建出一顆離屏的DOM樹。 之前在說FilberNode的屬性時&#xff0c;我們…

zabbix案例--zabbix監控Tomcat

目錄 一、 部署tomcat 二、配置zabbix-java-gateway 三、配置zabbix-server 四、配置zabbix-web界面 一、 部署tomcat tar xf apache-tomcat-8.5.16.tar.gz -C /usr/local/ ln -sv /usr/local/apache-tomcat-8.5.16/ /usr/local/tomcat cd /usr/local/tomcat/bin開啟JMX…

Vscode 常用操作教程

一、語言換成中文 這是我們可以直接點擊左邊欄第四個圖標搜索插件 chinese ,也可以直接ctrlshiftp快捷鍵也會出來如圖所示圖標&#xff0c;出來chinese 插件之后選擇安裝install,安裝完成之后重新ctrlshiftp會出現如圖所示頁面 找到我的鼠標在的地方對應的中文&#xff0c;此時…

win10下如何安裝ffmpeg

安裝ffmpeg之前先安裝win10 綠色軟件管理軟件&#xff1a;scoop. Scoop的基本介紹 Scoop是一款適用于Windows平臺的命令行軟件&#xff08;包&#xff09;管理工具&#xff0c;這里是Github介紹頁。簡單來說&#xff0c;就是可以通過命令行工具&#xff08;PowerShell、CMD等…

VVIC-商品詳情

一、接口參數說明&#xff1a; item_get-根據ID取商品詳情&#xff0c;點擊更多API調試&#xff0c;請移步注冊API賬號點擊獲取測試key和secret 公共參數 請求地址: https://api-gw.onebound.cn/vvic/item_get 名稱類型必須描述keyString是調用key&#xff08;點擊獲取測試k…

第一百一十三回 dart中的getter/setter方法

文章目錄 概念介紹使用方法示例代碼使用擴展 我們在上一章回中介紹了 flutter_screenutil包相關的內容&#xff0c;本章回中將介紹 dart中的setter/getter方法.閑話休提&#xff0c;讓我們一起Talk Flutter吧。 概念介紹 我們在這里介紹的setter/getter方法屬于編程語言中的…

【MongoDB】索引

目錄 一、概述 二、索引的類型 1、單字段索引 2、復合索引 3、其他索引 三、索引的管理 1、索引的創建 2、索引的查看 3、索引的刪除 四、索引的使用 1、執行計劃 2、涵蓋的查詢 一、概述 索引支持在MongoDB中高效地執行查詢。如果沒有索引&#xff0c;MongoDB必須…

Kubernetes pod調度約束[親和性 污點] 生命階段 排障手段

調度約束 Kubernetes 是通過 List-Watch 的機制進行每個組件的協作&#xff0c;保持數據同步的&#xff0c;每個組件之間的設計實現了解耦。 用戶是通過 kubectl 根據配置文件&#xff0c;向 APIServer 發送命令&#xff0c;在 Node 節點上面建立 Pod 和 Container。 APIServer…

springcloud3 hystrix實現服務降級,熔斷,限流以及案例配置

一 hystrix的作用 1.1 降級&#xff0c;熔斷&#xff0c;限流 1.服務降級&#xff1a; A方案出現問題&#xff0c;切換到兜底方案B&#xff1b; 2.服務熔斷&#xff1a;觸發規則&#xff0c;出現斷電限閘&#xff0c;服務降級 3.服務限流&#xff1a;限制請求數量。 二 案例…

ES6學習-Symbol

Symbol 數據類型Symbol&#xff0c;表示獨一無二的值。 對象的屬性名可有兩種類型&#xff0c;一種是原來的字符串&#xff0c;另一種是新增的 Symbol 類型 可以保證不與其他屬性名產生沖突。 let s1 Symbol() let s2 Symbol() console.log(s1, s2, s1 s2)//Symbol() Sy…

liunx exercise

云計算作業 Linux DAY1 1、創建alan1用戶&#xff0c;并使用root用戶切換用戶至alan1用戶。&#xff08;兩種方式切換【加-與不加-】&#xff0c;并總結其效果&#xff09; [rootlocalhost ~]# useradd alan1 [rootlocalhost ~]# su alan1 [alan1localhost root]$ pwd /roo…

FPGA學習——驅動WS2812光源并進行動態顯示

文章目錄 一、WS2812手冊分析1.1 WS2812燈源特性及概述1.2 手冊重點內容分析1.2.1 產品概述1.2.2 碼型及24bit數據設計 二、系統設計2.1 模塊設計2.2 模塊分析2.2.1 驅動模塊2.2.1 數據控制模塊 三、IP核設置及項目源碼3.1 MIF文件設計3.2 ROM IP核調用3.3 FIFO IP核調用3.4 項…