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