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