【Java 并发编程】线程池机制 ( 测试线程开销 | 启动线程分析 | 用户态 | 内核态 | 用户线程 | 内核线程 | 轻量级进程 )
文章目錄
- 一、測試線程開銷
- 1、正常測試
- 2、不創建線程
- 3、只創建不啟動線程
- 4、只啟動不等待執行完成
- 二、分析測試結果
- 1、啟動線程分析
- 2、用戶線程與內核線程
- 3、輕量級進程
- 4、驗證 Java 線程類型
一、測試線程開銷
線程池是線程的緩存 , 在 Java 高并發場景中 , 所有的異步操作 , 都可以使用線程池 ;
使用線程池時 , 不建議用在 " 執行耗時較長的操作 " 的業務場景中 ;
線程池機制 最重要的功能就是 復用線程 ; 線程的創建 , 銷毀 , 都是要消耗資源的 , 如果頻繁創建銷毀線程 , 會消耗很多資源 ;
1、正常測試
下面開始測試一下線程創建的開銷 :
在主線程中 , 啟動 101010 萬個線程 , 每個線程中累加 count 變量 ;
public class Main {/*** 線程中對該值進行累加操作*/private static int count = 0;public static void main(String[] args) throws InterruptedException {// 記錄程序開始執行時間long startTime = System.currentTimeMillis();// 創建 10 萬個線程, 開啟線程后, 向集合中添加一個元素for (int i = 0; i < 100000; i ++) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {count ++;}});// 啟動線程thread.start();// 等待線程執行完成thread.join();}// 記錄程序執行結束時間long endTime = System.currentTimeMillis();// 打印消耗的時間System.out.println("耗時 : " + ( endTime - startTime ) + " ms , 最終 count = " + count);} }執行結果 : 101010 萬個線程執行完畢消耗 10.99210.99210.992 秒 ;
2、不創建線程
注釋掉線程相關代碼 :
public class Main {/*** 線程中對該值進行累加操作*/private static int count = 0;public static void main(String[] args) throws InterruptedException {// 記錄程序開始執行時間long startTime = System.currentTimeMillis();// 創建 10 萬個線程, 開啟線程后, 向集合中添加一個元素for (int i = 0; i < 100000; i ++) {/*Thread thread = new Thread(new Runnable() {@Overridepublic void run() {count ++;}});// 啟動線程thread.start();// 等待線程執行完成thread.join();*/}// 記錄程序執行結束時間long endTime = System.currentTimeMillis();// 打印消耗的時間System.out.println("耗時 : " + ( endTime - startTime ) + " ms , 最終 count = " + count);} }執行結果 : 111 ms 執行完畢 ; 說明耗時操作是在 for 循環中 ;
3、只創建不啟動線程
注釋掉線程啟動代碼 :
public class Main {/*** 線程中對該值進行累加操作*/private static int count = 0;public static void main(String[] args) throws InterruptedException {// 記錄程序開始執行時間long startTime = System.currentTimeMillis();// 創建 10 萬個線程, 開啟線程后, 向集合中添加一個元素for (int i = 0; i < 100000; i ++) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {count ++;}});/*// 啟動線程thread.start();// 等待線程執行完成thread.join();*/}// 記錄程序執行結束時間long endTime = System.currentTimeMillis();// 打印消耗的時間System.out.println("耗時 : " + ( endTime - startTime ) + " ms , 最終 count = " + count);} }執行結果 : 耗時 797979 ms , 也很快 , 大部分時間都在 啟動 與 等待線程執行完畢消耗 ;
4、只啟動不等待執行完成
注釋掉等待線程執行完成代碼 :
public class Main {/*** 線程中對該值進行累加操作*/private static int count = 0;public static void main(String[] args) throws InterruptedException {// 記錄程序開始執行時間long startTime = System.currentTimeMillis();// 創建 10 萬個線程, 開啟線程后, 向集合中添加一個元素for (int i = 0; i < 100000; i ++) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {count ++;}});// 啟動線程thread.start();// 等待線程執行完成//thread.join();}// 記錄程序執行結束時間long endTime = System.currentTimeMillis();// 打印消耗的時間System.out.println("耗時 : " + ( endTime - startTime ) + " ms , 最終 count = " + count);} }執行結果 : 耗時 3.8663.8663.866 秒 ;
二、分析測試結果
1、啟動線程分析
在上述測試中 , 如果只是創建 101010 萬個 Thread 對象 , 這些在 Java 中就是普通的對象 ;
但是如果調用了 Thread 對象的 start() 方法 , 就要涉及到系統的線程切換 , 這個操作非常耗時 ;
操作系統的空間 , 分為 用戶空間 和 內核空間 ;
用戶空間中 , 有多個進程 , 每個進程有多個線程 , 每個進程都有一個 線程表 , 用于保存該進程中的線程 ;
JVM 創建的線程是 內核線程 ;
執行 main 函數時 , 處于 用戶態 , 一旦調用了 start() 方法啟動了線程 , 此時就進入了 內核態 , 該狀態切換消耗巨大 ;
2、用戶線程與內核線程
系統的線程分為 用戶線程 和 內核線程 ;
用戶線程 : 用戶線程是 用戶程序實現的線程 , 并負責管理線程的 創建 , 執行 , 調度 , 同步 ;
- 線程阻塞時 , 進程也會阻塞 ;
( Java 沒有用到用戶線程 )
內核線程 : 內核線程是 由內核管理的線程 , 其內部保存了線程的狀態信息 , 上下文信息 , 如果頻繁的切換線程 , 需要反復處理狀態信息 , 上下文信息 , 會浪費很多資源 ;
- 線程阻塞時 , 進程不會阻塞 ;
- 內核線程效率比用戶線程低 , 比進程高 ;
3、輕量級進程
輕量級進程 : 在我們寫的程序中 , 雖然使用了內核線程 , 但 沒有直接使用 , 而是 通過內核線程的高級接口使用內核線程 , 這個高級接口就是 " 輕量級進程 " , Java 程序中的 Thread 就是輕量級進程 , 每個 輕量級進程 都對應一個 內核線程 ;
4、驗證 Java 線程類型
在任務管理器中可以查看線程數 :
執行下面的程序 : 創建了 100001000010000 個線程
public class Test {public static void main(String[] args) {for (int i = 0; i < 10000; i ++){new Thread(()->{try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}}).start();}} }創建 100001000010000 線程后 , 發現線程數增加了 100001000010000 ;
由此可見 , Java 虛擬機創建的線程是內核線程 ;
Java 虛擬機創建線程 , 依賴于系統內核 , 內核空間的內核線程 與 用戶空間的 Java 線程 是一一對應的關系 ;
總結
以上是生活随笔為你收集整理的【Java 并发编程】线程池机制 ( 测试线程开销 | 启动线程分析 | 用户态 | 内核态 | 用户线程 | 内核线程 | 轻量级进程 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【错误记录】SeeMusic 一直卡在主
- 下一篇: 【Java 并发编程】线程池机制 ( 线