最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot3中使用虛擬線程的詳細(xì)過程

 更新時(shí)間:2025年06月06日 11:13:28   作者:颯颯的宇y  
虛擬線程主要解決的問題是減少I/O密集型任務(wù)的I/O阻塞,這篇文章主要介紹了SpringBoot3中使用虛擬線程的詳細(xì)過程,需要的朋友可以參考下

Java21、SpringBoot3中使用虛擬線程

引言

最近有讀取文件中數(shù)據(jù)的需求,且數(shù)據(jù)量百萬至千萬,普通的多線程讀取方式還是很慢。遂想到Java21虛擬線程,在網(wǎng)上學(xué)習(xí)了一圈,簡單的在這里記一個(gè)筆記,方便日后查找。

以下內(nèi)容是個(gè)人理解,注解辨別。

一、什么是虛擬線程,有什么用

1、虛擬線程是Java19提出來的一個(gè)概念,Java19提供特性預(yù)覽,開放實(shí)裝是Java21(2023年9月),目前來說(2024年3月),還是一個(gè)比較新的特性,對(duì)于一些不是很常見的庫可能尚未適配。

2、虛擬線程主要解決的問題是減少I/O密集型任務(wù)I/O阻塞。傳統(tǒng)的多線程在處理I/O的時(shí)候,例如某個(gè)線程在處理某個(gè)任務(wù),如果遇到I/O例如網(wǎng)絡(luò)通信、文件讀取,受限于網(wǎng)絡(luò)速度、機(jī)器的硬盤I/O速度等,這個(gè)線程會(huì)阻塞等待I/O的完成,然后再繼續(xù)往下執(zhí)行任務(wù)。這就帶來了一個(gè)問題,由于存在I/O過程,這個(gè)線程它不干活了,就在那里阻塞干等I/O完成,偷懶了一會(huì)兒,導(dǎo)致了CPU的利用率其實(shí)沒有達(dá)到理想狀態(tài)。

3、而虛擬線程則是在線程遇到I/O阻塞時(shí),會(huì)放暫時(shí)放棄等待去做其他事,等I/O完成它才會(huì)回來繼續(xù)執(zhí)行任務(wù),無疑這種方式提高了CPU的利用率,讓它無法再偷懶。
虛擬線程是由JVM來管理的,不由系統(tǒng)管理,并且它十分輕量,虛擬線程之間的切換開銷十分的小,所以你甚至可以開啟上百萬個(gè)虛擬線程。

4、虛擬線程并沒有增加實(shí)際的CPU可用線程,而是增加了線程的利用率,所以在面對(duì)CPU密集型任務(wù)時(shí),如數(shù)學(xué)計(jì)算等,它與傳統(tǒng)的線程沒有區(qū)別,因?yàn)?code>CPU密集型任務(wù)不存在大量的I/O等待。
如果你的需求中存在大量I/O等待導(dǎo)致性能瓶頸,那么可以考慮使用虛擬線程。

二、JavaSE中使用虛擬線程

官方對(duì)虛擬線程做了很多封裝,你可以很簡單的使用它

Thread.ofVirtual().start(() -> {
	//異步任務(wù),這里的參數(shù)不用lambda表達(dá)式的話要傳一個(gè)實(shí)現(xiàn)Runnable的類
}); //開啟一個(gè)虛擬線程

部分源代碼:

public static Builder.OfVirtual ofVirtual() {
        return new ThreadBuilders.VirtualThreadBuilder();
}
public Thread start(Runnable task) {
            Thread thread = unstarted(task);
            thread.start();
            return thread;
}

同時(shí),官方還提供了使用平臺(tái)線程(真實(shí)線程)的方法:

Thread.ofPlatform().start(() -> {
	//異步任務(wù),這里的參數(shù)不用lambda表達(dá)式的話要傳一個(gè)實(shí)現(xiàn)Runnable的類
}); //開啟一個(gè)平臺(tái)線程

當(dāng)然,在實(shí)際使用中,我們不可能只使用一個(gè)虛擬線程,所以官方提供了一個(gè)類似于線程池的方法:

var executors = Executors.newVirtualThreadPerTaskExecutor() //開啟一個(gè)“虛擬線程池”
executors.submit(() -> {
	//異步任務(wù),這里的參數(shù)不用lambda表達(dá)式的話要傳一個(gè)實(shí)現(xiàn)Runnable的類
}); //向“虛擬線程池”中提交一個(gè)任務(wù)

說明一下:
首先var關(guān)鍵字是Java10引入的,類似于C++中的auto,它可以自動(dòng)類型推斷,就是說我們以后可以不用寫類似 ArrayList<String> list = new ArrayList<>(); 這樣的代碼了,直接 var list = new ArrayList<String>(); 編譯器會(huì)自己類型推斷,但是這么寫你得注意變量名,不然讀起來會(huì)很麻煩。還有var關(guān)鍵字只能用于局部變量,成員變量、形參等都不行。

這種方式用虛擬線程,JVM會(huì)自動(dòng)利用機(jī)器可用的真實(shí)線程來跑這些虛擬線程,不用額外配置

使用“虛擬線程池”的時(shí)候最好搭配try-with-resource,以達(dá)到資源的自動(dòng)釋放:

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
	for (int i = 0; i < 1000000; i++) {
	    int finalI = i;
	    var future = executor.submit(() -> {
	        System.out.println("任務(wù) " + finalI +" 運(yùn)行在 " + Thread.currentThread());
	        TimeUnit.SECONDS.sleep(3);//模擬阻塞
	        // 執(zhí)行其他操作
	        return "ok";
	    });
	}
}

你可以在下方使用 Future.get() 的方式來等待線程執(zhí)行完畢

以上代碼執(zhí)行結(jié)果:

...
任務(wù) 992 運(yùn)行在 VirtualThread[#1038]/runnable@ForkJoinPool-1-worker-12
任務(wù) 578 運(yùn)行在 VirtualThread[#622]/runnable@ForkJoinPool-1-worker-6
任務(wù) 802 運(yùn)行在 VirtualThread[#848]/runnable@ForkJoinPool-1-worker-4
任務(wù) 997 運(yùn)行在 VirtualThread[#1043]/runnable@ForkJoinPool-1-worker-13
...

我們可以看到線程名成為了 VirtualThread ,它們運(yùn)行在不同的真實(shí)線程上 worker-13 、 worker-4

三、SpringBoot中使用虛擬線程

在SpringBoot中使用虛擬線程要求SpringBoot的版本最低是 3.x ,JDK的版本不能低于21

*** 注意,暫時(shí)不要用Undertow作為Servlet容器來使用虛擬線程,可能會(huì)有內(nèi)存溢出等問題,你的內(nèi)存占用會(huì)飆得老高,SpringBoot官方目前說的是Undertow的問題,另外還補(bǔ)充說如果這個(gè)問題沒能得到解決的話,未來可能移除對(duì)Undertow的虛擬線程支持,所以暫時(shí)別在生產(chǎn)環(huán)境中使用。官方GitHub issues 原文地址

1、使用

使用Jdk21,Maven引入SpringBoot3.x

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>3.2.1</version>
</dependency>

新建一個(gè)配置類,添加兩個(gè)Bean:

@Configuration
public class Config {
    @Bean
    public AsyncTaskExecutor asyncTaskExecutor(){
        return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor());
    }
    @Bean
    public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
        return protocolHandler -> protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
    }
}

說明一下,第一個(gè)Bean AsyncTaskExecutor 是為了讓SpringBoot在你使用 @Async 注解的時(shí)候用虛擬線程執(zhí)行你的 @Async 任務(wù)。

第二個(gè)Bean TomcatProtocolHandlerCustomizer 是為了讓Tomcat用虛擬線程來接請求的。我們可以測試一下,寫一個(gè)Controller和一個(gè)Service:

@RestController
@RequestMapping("/base")
public class TestController {
    @Resource
    private TestService service;
    @GetMapping("/test")
    public String test(){
        System.out.println("---> " + Thread.currentThread());
        service.test();
        return "ok";
    }
}
@EnableAsync
@Service
public class TestService {
    @Async
    public void test(){
        System.out.println("================> " + Thread.currentThread());
    }
}

運(yùn)行這個(gè)服務(wù),訪問 localhost:8080/base/test 接口的時(shí)候,會(huì)打印:

---> VirtualThread[#65]/runnable@ForkJoinPool-1-worker-1
================> VirtualThread[#71]/runnable@ForkJoinPool-1-worker-4

發(fā)現(xiàn)都是虛擬線程,我們?nèi)偛判陆ǖ呐渲妙愔凶⑨尩粝旅娴?TomcatProtocolHandlerCustomizer 這個(gè)Bean,重啟服務(wù)再訪問接口:

---> Thread[#61,http-nio-8080-exec-1,5,main]
================> VirtualThread[#78]/runnable@ForkJoinPool-1-worker-1

會(huì)發(fā)現(xiàn)接收請求的線程不是虛擬線程了,同樣的,注釋掉上面的 AsyncTaskExecutor 那么我們用 @Async 注解的方法也是不使用虛擬線程了。

如果你想提高請求處理量的話,就注入下面這個(gè)Bean,只是想使用虛擬線程做其他事的話,下面這個(gè)Bean是可選的。

2、線程池問題

如果你的打印內(nèi)容出現(xiàn):

.s.a.AnnotationAsyncExecutionInterceptor : More than one TaskExecutor bean found within the context,
 and none is named 'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly as an alias) 
 in order to use it for async processing: [asyncTaskExecutor, taskScheduler]

或者你打印的線程信息類似:

Thread[#129,SimpleAsyncTaskExecutor-2,5,VirtualThreads]

第一個(gè)警告的意思是在上下文中找到多個(gè)TaskExecutor bean,但沒有一個(gè)名稱為“TaskExecutor”。將其中一個(gè)標(biāo)記為主任務(wù)或?qū)⑵涿麨?ldquo;taskExecutor”(可能是別名),以便將其用于異步處理:[asyncTaskExecutor,taskScheduler]
問題是項(xiàng)目中引入的線程池過多,Spring容器不知道用哪一個(gè),我們只需要在AsyncTaskExecutor這個(gè)Bean上添加一個(gè)@Primary將我們設(shè)置的線程池設(shè)置為主線程池就行了:

@Bean
@Primary // 將我們設(shè)置的虛擬線程池作為主線程池
public AsyncTaskExecutor asyncTaskExecutor(){
	return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor());
}

四、總結(jié)

目前我沒有做具體的性能測試,有興趣的可以做做壓測。后續(xù)我做了測試的話會(huì)更新這篇博客。
虛擬線程目前來說是一個(gè)很新的特性,可能會(huì)有很多問題尚未發(fā)現(xiàn),使用要謹(jǐn)慎。
此外,CPU密集型任務(wù)目前來說就不建議用虛擬線程了,性能沒提升。

參考文獻(xiàn)

1、簡單記錄下 Spring Boot 使用虛擬線程Virtual Threads(Java的協(xié)程)的方法
2、Java21手冊(一):虛擬線程 Virtual Threads

到此這篇關(guān)于SpringBoot3中使用虛擬線程的文章就介紹到這了,更多相關(guān)SpringBoot使用虛擬線程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java編程技巧:if-else優(yōu)化實(shí)踐總結(jié)歸納

    Java編程技巧:if-else優(yōu)化實(shí)踐總結(jié)歸納

    這篇文章主要介紹了Java中避免過多if-else的幾種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2021-06-06
  • 老生常談spring的事務(wù)傳播機(jī)制

    老生常談spring的事務(wù)傳播機(jī)制

    這篇文章主要介紹了spring的事務(wù)傳播機(jī)制,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Java8優(yōu)雅的字符串拼接工具類StringJoiner實(shí)例代碼

    Java8優(yōu)雅的字符串拼接工具類StringJoiner實(shí)例代碼

    這篇文章主要給大家介紹了關(guān)于Java8優(yōu)雅的字符串拼接工具類StringJoiner的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • JAVA Comparator 和 Comparable接口使用方法

    JAVA Comparator 和 Comparable接口使用方法

    本文介紹了Java中Comparable和Comparator接口的使用,包括它們的定義、方法和應(yīng)用場景,Comparable用于定義類的自然排序規(guī)則,而Comparator提供了一種靈活的方式來定義對(duì)象之間的排序規(guī)則,無需修改類本身,感興趣的朋友一起看看吧
    2025-03-03
  • IDEA搭建純注解版本SpringMVC的web開發(fā)環(huán)境全過程并分析啟動(dòng)原理

    IDEA搭建純注解版本SpringMVC的web開發(fā)環(huán)境全過程并分析啟動(dòng)原理

    本文詳細(xì)介紹了如何使用注解開發(fā)搭建Spring Web環(huán)境,包括創(chuàng)建Maven工程、配置web環(huán)境、設(shè)置pom.xml、創(chuàng)建配置類和控制器等步驟,同時(shí),文章還探討了注解開發(fā)中如何創(chuàng)建IOC容器和添加DispatcherServlet組件,并通過Servlet 3.0規(guī)范
    2024-11-11
  • Java多線程 Callable、Future 和FutureTask

    Java多線程 Callable、Future 和FutureTask

    這篇文章主要介紹Java多線程中的 Callable、Future 以及FutureTask,下面文章圍繞Java多線程的相關(guān)資料展開全文詳細(xì)內(nèi)容,需要的朋友可以參考一下
    2021-10-10
  • 認(rèn)證授權(quán)中解決AuthenticationManager無法注入問題

    認(rèn)證授權(quán)中解決AuthenticationManager無法注入問題

    這篇文章主要介紹了認(rèn)證授權(quán)中解決AuthenticationManager無法注入問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-10-10
  • 實(shí)踐講解SpringBoot自定義初始化Bean+HashMap優(yōu)化策略模式

    實(shí)踐講解SpringBoot自定義初始化Bean+HashMap優(yōu)化策略模式

    本篇講解了SpringBoot自定義初始化Bean+HashMap優(yōu)化策略模式,通過實(shí)踐的方式更通俗易懂,對(duì)此不了解的同學(xué)跟著小編往下看吧
    2021-09-09
  • java實(shí)現(xiàn)俄羅斯方塊游戲

    java實(shí)現(xiàn)俄羅斯方塊游戲

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)俄羅斯方塊游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • IDEA2020.1同步系統(tǒng)設(shè)置到GitHub的方法

    IDEA2020.1同步系統(tǒng)設(shè)置到GitHub的方法

    這篇文章主要介紹了IDEA2020.1同步系統(tǒng)設(shè)置到GitHub的方法,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-05-05

最新評(píng)論

临高县| 全椒县| 井研县| 长寿区| 黄骅市| 平武县| 平陆县| 郎溪县| 克什克腾旗| 神池县| 正安县| 会东县| 乌审旗| 尉氏县| 宁夏| 小金县| 乌鲁木齐县| 仪陇县| 东辽县| 柳州市| 二连浩特市| 那曲县| 略阳县| 余干县| 东安县| 定远县| 商城县| 家居| 库伦旗| 黔西| 镇坪县| 临漳县| 桂东县| 卓资县| 静海县| 板桥市| 英吉沙县| 庆元县| 巴楚县| 天全县| 凤山市|