線程組和線程池的基本用法

1.線程組

1.1創建線程組的方法

public class xianchengzu {public static void main(String[] args) {ThreadGroup group = new ThreadGroup("group");// 創建線程組時指定父線程組ThreadGroup parent = new ThreadGroup("parent");ThreadGroup child = new ThreadGroup(parent, "child");// 向線程組中添加線程Thread thread1 = new Thread(group,"thread1"){public void run(){System.out.println("我是" + group.getName() + "線程組的一個線程,名稱為:"+ this.getName());}};thread1.start();// 通過runnable創建線程Thread thread2 = new Thread(group,new run());thread2.start();}
}class run implements Runnable{public void run(){System.out.println("我是實現runnable接口的線程");}
}

1.2線程組常用方法

// 獲取線程組名稱
String name = group.getName();// 獲取線程組中活躍線程的估計數
int activeCount = group.activeCount();// 獲取線程組中活躍子線程組的估計數
int activeGroupCount = group.activeGroupCount();// 中斷線程組中所有線程
group.interrupt();// 設置線程組中所有線程的優先級
group.setMaxPriority(Thread.NORM_PRIORITY);// 枚舉線程組中的線程
Thread[] threads = new Thread[group.activeCount()];
group.enumerate(threads); // 將線程組中的線程復制到數組中

2.線程池

線程池是一個包含了能提供相同功能的多個線程的集合。

2.1ThreadPoolExecutor

ava 線程池的核心實現是ThreadPoolExecutor,其構造方法包含以下核心參數:

public ThreadPoolExecutor(int corePoolSize,                // 核心線程數int maximumPoolSize,             // 最大線程數long keepAliveTime,              // 非核心線程空閑超時時間TimeUnit unit,                   // 超時時間單位BlockingQueue<Runnable> workQueue,// 任務阻塞隊列ThreadFactory threadFactory,     // 線程工廠RejectedExecutionHandler handler // 拒絕策略
)

各參數的含義:

  • corePoolSize:線程池長期保持的線程數,即使線程空閑也不會銷毀
  • maximumPoolSize:線程池允許創建的最大線程數
  • keepAliveTime:當線程數超過核心線程數時,多余線程的空閑存活時間
  • workQueue:用于存放等待執行的任務的阻塞隊列
  • threadFactory:用于創建新線程的工廠
  • handler:當任務無法被處理時的拒絕策略
public class xianchengchi {public static void main(String[] args) {ThreadPoolExecutor pool01 = new ThreadPoolExecutor(3,5,500,TimeUnit.MILLISECONDS,new ArrayBlockingQueue<Runnable>(5));for(int i=0;i<10;++i){DoWork doWork = new DoWork(i);pool01.execute(doWork);System.out.println("我是線程池,當前池中的線程數為:"+pool01.getActiveCount()+ " 線程池的總容量為:"+pool01.getPoolSize());}for(int i=0;i<8;++i){System.out.println("我是01線程池,目前池內線程總數為:"+ pool01.getPoolSize() + " 等待隊列中的任務為:"+pool01.getQueue().size());try{Thread.currentThread().sleep(100);}catch (InterruptedException e){e.printStackTrace();}}pool01.shutdown();}
}class DoWork implements Runnable {private int i;public DoWork(int i) {this.i = i;}@Overridepublic void run() {System.out.println("我是" + i + "號任務,我的任務執行完了");try{Thread.currentThread().sleep(100);}catch (InterruptedException e){e.printStackTrace();}}
}

上面代碼創建了一個線程池pool01,核心線程數為3,最大線程數為5,空閑存活時間為500毫秒,等待隊列為5。

為線程池添加10個線程,最初,線程池的核心線程數從0到3,此時新加入的任務會被放入等待隊列中,如果等待隊列也滿了,才會繼續增加線程池的線程數,如果線程池達到最大的線程數時繼續加入線程,則會調用拒絕策略。

下面是一次運行的結果。

我是0號任務,我的任務執行完了
我是線程池,當前池中的線程數為:1 線程池的總容量為:1
我是線程池,當前池中的線程數為:2 線程池的總容量為:2
我是線程池,當前池中的線程數為:3 線程池的總容量為:3
我是1號任務,我的任務執行完了
我是線程池,當前池中的線程數為:3 線程池的總容量為:3
我是線程池,當前池中的線程數為:3 線程池的總容量為:3
我是2號任務,我的任務執行完了
我是線程池,當前池中的線程數為:3 線程池的總容量為:3
我是線程池,當前池中的線程數為:3 線程池的總容量為:3
我是線程池,當前池中的線程數為:3 線程池的總容量為:3
我是線程池,當前池中的線程數為:4 線程池的總容量為:4
我是8號任務,我的任務執行完了
我是線程池,當前池中的線程數為:5 線程池的總容量為:5
我是9號任務,我的任務執行完了
我是01線程池,目前池內線程總數為:5 等待隊列中的任務為:5
我是4號任務,我的任務執行完了
我是3號任務,我的任務執行完了
我是5號任務,我的任務執行完了
我是6號任務,我的任務執行完了
我是01線程池,目前池內線程總數為:5 等待隊列中的任務為:1
我是7號任務,我的任務執行完了
我是01線程池,目前池內線程總數為:5 等待隊列中的任務為:0
我是01線程池,目前池內線程總數為:5 等待隊列中的任務為:0
我是01線程池,目前池內線程總數為:5 等待隊列中的任務為:0
我是01線程池,目前池內線程總數為:5 等待隊列中的任務為:0
我是01線程池,目前池內線程總數為:5 等待隊列中的任務為:0
我是01線程池,目前池內線程總數為:3 等待隊列中的任務為:0進程已結束,退出代碼為 0

2.2四種常用方法

除了前面介紹的通過ThreadPoolExecutor構建方法創建的線程池外,還有四種簡化的創建線程池的方法,這幾種方法不需要設置大量的參數,或者當不清楚怎么設置參數時可以參考這些方法。

2.2.1FixedThreadPool(固定大小的線程池)

  • 創建方式:通過Executors.newFixedThreadPool(int nThreads)方法創建,參數nThreads指定線程池中線程的數量。
  • 核心參數特點
    • corePoolSizemaximumPoolSize都等于創建時指定的線程數量,即線程池中的線程數量固定不變。
    • keepAliveTime為 0,因為線程不會被銷毀,始終保持在池中。
    • 使用LinkedBlockingQueue作為任務隊列,容量為Integer.MAX_VALUE,理論上可以存放無限多的任務。
  • 工作原理:當提交任務時,如果有空閑線程,就直接使用空閑線程執行任務;如果沒有空閑線程,任務會被放入任務隊列等待,直到有線程空閑。
  • 適用場景:適用于執行長期的、有穩定并發需求的任務,比如服務器端的請求處理,因為固定數量的線程可以保證系統資源的穩定消耗,避免因線程數量波動帶來的性能問題。

2.2.2CachedThreadPool(可緩存的線程池)

  • 創建方式:通過Executors.newCachedThreadPool()方法創建。
  • 核心參數特點
    • corePoolSize為 0,maximumPoolSizeInteger.MAX_VALUE,即線程池中的線程數量可以根據任務的多少動態調整。
    • keepAliveTime為 60L,單位是TimeUnit.SECONDS,即非核心線程如果閑置 60 秒,就會被銷毀。
    • 使用SynchronousQueue作為任務隊列,這個隊列不存儲任務,每個插入操作必須等待另一個線程的移除操作。
  • 工作原理:當提交任務時,如果有空閑線程,就直接使用空閑線程執行任務;如果沒有空閑線程,就創建新的線程來執行任務。當線程空閑時間超過 60 秒,就會被銷毀,所以線程池中的線程數量會根據任務的提交情況動態增減。
  • 適用場景:適用于執行大量的短期異步任務,比如異步日志記錄、異步數據處理等場景。由于它可以快速創建和銷毀線程,能很好地應對突發的大量任務請求。

2.2.3SingleThreadExecutor(單線程的線程池)

  • 創建方式:通過Executors.newSingleThreadExecutor()方法創建。
  • 核心參數特點
    • corePoolSizemaximumPoolSize都為 1,即線程池中始終只有一個線程。
    • keepAliveTime為 0,線程不會被銷毀。
    • 使用LinkedBlockingQueue作為任務隊列,容量為Integer.MAX_VALUE
  • 工作原理:所有提交的任務都會按照順序依次由這個唯一的線程來執行,前一個任務執行完后,才會執行下一個任務,保證了任務的串行執行。
  • 適用場景:適用于需要保證任務順序執行,或者任務之間有依賴關系的場景,比如數據庫的單線程操作,確保數據操作的一致性和順序性 。

2.2.4ScheduledThreadPool(支持定時及周期性任務執行的線程池)

  • 創建方式:通過Executors.newScheduledThreadPool(int corePoolSize)方法創建,參數corePoolSize指定線程池中的核心線程數量。
  • 核心參數特點
    • corePoolSize是創建時指定的核心線程數量,maximumPoolSizeInteger.MAX_VALUE
    • keepAliveTime為 0,核心線程不會被銷毀。
    • 使用DelayedWorkQueue作為任務隊列,用于存儲延遲執行的任務。
  • 工作原理:除了具備普通線程池提交任務執行的功能外,還支持定時執行任務(如延遲一定時間后執行)和周期性執行任務(如每隔固定時間執行一次)。
  • 適用場景:適用于需要定時執行或者周期性執行的任務,比如定時備份數據、定時發送郵件、周期性的系統狀態檢查等場景。

3.線程的異常處理

使用Thread類的線程或是實現了Runnable接口的線程需要在 run 方法中使用try-catch捕獲錯誤,在主程序中捕獲會失效,但是實現callable接口的線程可以在主程序中捕獲錯誤。

原因是在Thread類中有一個接口 UncaughtExceptionHandler,它包含一個uncaughtException方法,該方法原意是專門對原本線程中沒有捕獲成功的異常進行最終捕獲處理,同時,當線程未捕獲異常而進入該方法時,所有拋出的異常都會被java虛擬機所忽略,即不再對外拋出。

由于java的Thread類默認使用uncaughtException進行空處理,而java虛擬機又會忽略該方法之后的拋出異常,因此我們經常看到的結果是內部線程發生異常時,在外層線程看來,是既不能成功對內部線程的異常進行catch,也不能獲取其詳細信息。

根本原因是:子線程和主線程是完全獨立的執行流(擁有各自的調用棧),子線程中拋出的異常無法直接 “滲透” 到主線程的調用棧中

具體來說:????????

  • 當我們通過thread.start()啟動子線程時,子線程的run()方法會在一個全新的調用棧中執行(與主線程的調用棧完全分離)。
  • 如果run()方法中拋出未捕獲的異常(比如RuntimeException),這個異常只會在子線程的調用棧中傳播,不會影響主線程的執行流程。
  • 主線程的try-catch只能捕獲主線程自身調用棧中產生的異常,無法 “跨線程” 捕獲子線程的異常。

為什么 Callable 的異常可以在主程序捕獲?

CallableRunnable的核心區別在于:Callable的異常會被線程池 “封裝并保存”,通過Future對象傳遞給主線程

具體流程:

  1. Callablecall()方法允許聲明拋出異常(包括受檢異常),當它拋出異常時,線程池會捕獲這個異常,并將其封裝到Future對象中。
  2. 主線程調用future.get()獲取結果時,如果Callable執行中拋出了異常,get()方法會將封裝的異常以ExecutionException的形式拋出(ExecutionExceptiongetCause()方法可以獲取原始異常)。
  3. 因此,主線程可以通過try-catch捕獲ExecutionException,從而間接獲取Callable中的異常。

1. 使用Runnable接口的線程異常處理示例

public class RunnableExceptionExample {public static void main(String[] args) {Runnable runnable = () -> {// 模擬拋出異常throw new RuntimeException("Runnable中的異常");};Thread thread = new Thread(runnable);try {// 這里直接在main方法中捕獲,捕獲不到異常thread.start(); } catch (Exception e) {System.out.println("在main方法中捕獲Runnable異常: " + e.getMessage());}}
}

上述代碼中,在main方法里直接捕獲Runnable線程執行時拋出的異常是捕獲不到的。如果要處理Runnable線程中的異常,需要在run方法內部進行捕獲,修改代碼如下:

public class RunnableExceptionHandleInRunExample {public static void main(String[] args) {Runnable runnable = () -> {try {// 模擬拋出異常throw new RuntimeException("Runnable中的異常"); } catch (Exception e) {System.out.println("在Runnable的run方法中捕獲異常: " + e.getMessage());}};Thread thread = new Thread(runnable);thread.start(); }
}

2. 使用Callable接口的線程異常處理示例

public class FutureCatch {public static void main(String[] args) throws InterruptedException, ExecutionException {Callable<String> stringCallable = new subCallable();FutureTask<String> futureTask = new FutureTask<String>(stringCallable);Thread thread = new Thread(futureTask);thread.start();try{System.out.println(futureTask.get());}catch(Exception e){System.out.println("返回字符串失敗了,下標有問題");}}
}class subCallable implements Callable<String> {public String call() throws Exception{String str = "我是測試字符串";String subStr = str.substring(6,10);return subStr;}
}

4.多線程的安全關閉

多線程的關閉設計其他線程的運行和穩定,所以一個線程并不是調用一個關閉的方法就能馬上停止的,要考慮線程使用的資源是否已經釋放、線程是否需要在退出服務前通知其它線程或進行一些準備工作。

public class CloseThread {public static void main(String[] args) throws InterruptedException, ExecutionException {Thread clost = new Thread(new waittoclose());clost.start();try{// 讓主線程休眠Thread.sleep(2000);}catch (InterruptedException e){e.printStackTrace();}// 調用方法后isInterrupted() 變為true,并且拋出InterruptedExceptionclost.interrupt();}
}class waittoclose implements Runnable{@Overridepublic void run() {while(Thread.currentThread().isInterrupted()==false){try{System.out.println(Thread.currentThread().getName()+"正在運行");Thread.sleep(200);}catch(InterruptedException e){System.out.println("資源正在釋放");System.out.println("線程關閉中");Thread.currentThread().interrupt();}}}
}

當線程在執行Thread.sleep()?時,如果收到中斷信號(其他線程調用了它的?interrupt()?方法),會發生兩件事:

  1. sleep()?會立即拋出?InterruptedException?異常,提前結束休眠
  2. 自動清除線程的中斷狀態(將中斷標記設為?false

在Runnable接口中,try模塊的Thread.sleep(200)在察覺到interrupt后,會按照上述拋出異常和清除中斷狀態,之后catch模板捕獲異常,并進行善后操作,最后還要關閉一次線程,這樣線程才能實現安全關閉。

5.自定義多線程異常處理

這里給一個簡單的自定義異常:

public class ExceptionHandle implements Thread.UncaughtExceptionHandler {@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println(t.getName() + "拋出了異常信息:" + e.toString());}
}
  • 實現了 Thread.UncaughtExceptionHandler 接口
  • 當線程拋出未捕獲異常時,JVM會自動調用此方法

public class CatchExceptionFactory implements ThreadFactory {@Overridepublic Thread newThread(Runnable r) {Thread t = new Thread(r);t.setUncaughtExceptionHandler(new ExceptionHandle()); // 關鍵設置return t;}
}
  • 實現了 ThreadFactory 接口
  • 為每個新創建的線程設置自定義異常處理器

class divide implements Runnable {@Overridepublic void run() {int a = 30;int b = 3;int step = 1;while(b >= 0) {System.out.println(a + "與" + b + "相除的結果為:");System.out.println(a / b); // 當b=0時這里會拋出ArithmeticExceptionb--;}System.out.println("結束運算");}
}
  • 包含一個會引發除零異常的任務
  • 當b遞減到0時,a/b 會拋出 ArithmeticException

public class ExceptionTest {public static void main(String[] args) {// 使用自定義線程工廠創建線程池ExecutorService executorService = Executors.newCachedThreadPool(new CatchExceptionFactory());// 提交任務executorService.execute(new divide());}
}

運行結果(此時主程序并沒有停止):

30與3相除的結果為:
10
30與2相除的結果為:
15
30與1相除的結果為:
30
30與0相除的結果為:
Thread-0拋出了異常信息:java.lang.ArithmeticException: / by zero

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

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

相關文章

百度華為硬件筆試機試題-卷4

百度華為硬件筆試機試題-卷4 收集整理了以下30道選擇題和判斷題,涵蓋電源管理、功率放大、半導體器件、數字邏輯、信號處理和電磁理論等領域。題目涉及復雜計算和分析,給出了參考答案和詳細的解析,非常適合硬件工程師筆試機試準備。 選擇題 1. 電源紋波主要測量什么值? …

38-TS之類型保護

關注大師不迷路,大師帶你上高度~ 文章目錄 前言 一、類型保護是什么? 二、使用步驟 1. 使用 typeof 操作符 2. 使用 instanceof 操作符 3. 自定義類型保護函數 4. 使用 in 操作符 總結 前言 關注大師不迷路,大師帶你上高度~ 在前端開發中,JavaScript 的動態類型特性提供了…

win下安裝labelimg

1、anconda安裝python、qt的版本 conda create -n labelme python3.10.18 PyQt5 5.15.11 <pip> PyQt5-Qt5 5.15.2 <pip> PyQt5_sip 12.17.0 <p…

【Qt開發】常用控件(二) -> enabled

目錄 1 -> 什么是 enabled 屬性 2 -> API 3 -> 代碼示例 3.1 -> 創建禁用狀態按鈕 3.2 -> 通過按鈕切換按鈕的禁用狀態 1 -> 什么是 enabled 屬性 在 Qt 中&#xff0c;enabled 是 QWidget 類的一個基礎屬性&#xff0c;它控制控件是否對用戶輸入做出響…

MySQL 配置性能優化賽:核心策略與實戰技巧

在數據庫性能優化領域,MySQL 配置調優如同一場精密的競技比賽 —— 既要深刻理解數據庫內核機制,又要根據硬件環境和業務場景靈活調整參數,最終在性能指標上脫穎而出。本文將圍繞 MySQL 配置性能優化的核心維度,解析關鍵參數調優策略與實戰經驗。 一、性能優化的底層邏輯:…

C++ WonderTrader源碼分析之自旋鎖實現

一、介紹 在WonderTrader的文件SpinMutex.hpp定義了跨平臺的自旋鎖的實現。 二、實現原理 1、類 SpinMutex&#xff1a;自旋鎖實現SpinMutex 是一個輕量級的自旋鎖&#xff08;Spinlock&#xff09;實現&#xff0c;用于多線程之間保護臨界區資源。自旋鎖通過不斷嘗試獲取鎖而不…

【AI大模型】Spring AI 基于Redis實現對話持久存儲詳解

目錄 一、前言 二、Spring AI 會話記憶介紹 2.1 Spring AI 會話記憶概述 2.2 常用的會話記憶實現方式 2.2.1 集成數據庫持久存儲會話實現步驟 2.3 適用場景 三、Spring AI基于內存會話記憶存儲 3.1 本地開發環境準備 3.2 工程搭建與集成 3.2.1 添加核心依賴 3.3.2 添…

Numpy科學計算與數據分析:Numpy數據分析與圖像處理入門

Numpy實戰&#xff1a;從數據分析到圖像處理 學習目標 通過本課程&#xff0c;學員將學會運用Numpy庫進行數據分析和圖像處理。學習如何使用Numpy進行數據的高效處理&#xff0c;以及如何利用Numpy進行基本的圖像操作。 相關知識點 Numpy的數據分析和圖像處理 學習內容 1…

Vue框架總結案例

目錄 一、驗證用戶名是否已經被注冊過 二、過濾器 三、圖書管理系統 四、axios網絡請求 一、驗證用戶名是否已經被注冊過 1.案例 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><script src"j…

hyper-v虛擬機啟動失敗:Virtual Pci Express Port無法打開電源,因為發生錯誤,找不到即插即用設備

啟動錯誤 今天啟動某個hyper-v虛擬機時&#xff0c;啟動失敗了&#xff0c;大概的錯誤信息為&#xff1a;嘗試更改“ubuntu_desktop_2204”的狀態時應用程序遇到錯誤。Virtual Pci Express Port (實例 ID 0445948B-C377-4912-AEEB-58A3D45C5694): 無法開機&#xff0c;因…

CSS包含塊與百分比取值機制完全指南

引言&#xff1a;為什么需要理解包含塊&#xff1f; 在CSS布局的世界中&#xff0c;包含塊(Containing Block) 是一個基礎但至關重要的概念。它就像是一個隱形的參考框架&#xff0c;決定了元素如何定位、尺寸如何計算以及百分比值如何解析。許多CSS開發者在使用百分比單位時遇…

Numpy科學計算與數據分析:Numpy數組操作入門:合并、分割與重塑

Numpy數組操作實戰 學習目標 通過本課程的學習&#xff0c;學員將掌握Numpy中數組的基本操作&#xff0c;包括數組的合并、分割以及重塑等技巧&#xff0c;能夠靈活運用這些操作處理數據&#xff0c;為后續的科學計算和數據分析打下堅實的基礎。 相關知識點 Numpy數組操作 …

11_Mybatis 是如何進行DO類和數據庫字段的映射的?

11_Mybatis 是如何進行DO類和數據庫字段的映射的&#xff1f; 假設 VideoAbnormalContentMapper.xml 文件有如下方法&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN&quo…

2025年滲透測試面試題總結-06(題目+回答)

安全領域各種資源&#xff0c;學習文檔&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各種好玩的項目及好用的工具&#xff0c;歡迎關注。 目錄 邏輯漏洞 一、三大高危業務邏輯漏洞及修復方案 1. 訂單金額篡改&#xff08;參數操縱&#xff09; 2. 重…

SpringBoot激活指定profile的方式

題目詳細答案在 Spring Boot 中&#xff0c;可以通過多種方式激活指定的 Profile&#xff0c;以便在不同的環境中使用不同的配置。在application.properties文件中激活可以在默認的application.properties文件中通過spring.profiles.active屬性激活某個 Profile。# application…

Pytest項目_day10(接口的參數傳遞)

接口的參數傳遞 如果我們需要在一個測試用例中使用另一個測試用例中獲得的數據&#xff0c;應該怎么辦&#xff1f; 解決方案一&#xff1a;使用函數返回值 - 我們可以在另一個測試用例中使用return來返回所需的數據&#xff0c;并在其他的測試用例中調用該測試用例&#xff08…

深信服GO面試題及參考答案(上)

Go 和 Java 的特點和區別是什么? Go 和 Java 都是靜態類型、編譯型語言,但在設計理念、語法特性、并發模型等方面存在顯著差異,具體如下: 從語言設計目標來看,Go 由 Google 開發,旨在解決大型系統開發中的復雜性,強調“簡單、高效、并發”,語法簡潔,摒棄了許多傳統面向…

BGP筆記及綜合實驗

BGP基礎一、BGP產生背景 - BGP定義&#xff1a;邊界網關協議&#xff08;BGP&#xff09;是自治系統間的動態路由協議&#xff0c;屬于外部網關協議&#xff08;EGP&#xff09;。 - 自治系統&#xff08;AS&#xff09;&#xff1a;由統一管理、運行同一IGP協議的路由器組成&a…

全棧:如何判斷自己應該下載哪個版本的Tomcat

版本兼容性矩陣 https://tomcat.apache.org/whichversion.html https://tomcat.apache.org/download-11.cgi 介紹一下這些版本的不同點&#xff1a; 一、按系統選&#xff08;優先看這個&#xff09; 1.Windows 系統&#xff08;普通使用&#xff0c;非服務自啟&#xff09…

Redis的Linux安裝

可以直接命令下載 wget http://download.redis.io/releases/redis-5.0.4.tar.gz下載好之后解壓縮&#xff0c;并且重命名為redis 由于redis是c語言編寫的&#xff0c;所以我們需要先安裝gcc&#xff0c;安裝的命令如下&#xff1a;yum -y install gcc 安裝成功后輸入 : gcc -v…