欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

Java21、SpringBoot3中使用虛擬線程

引言

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

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

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

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

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

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

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

二、JavaSE中使用虛擬線程

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

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

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

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

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

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

使用“虛擬線程池”的時(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() 的方式來(lái)等待線程執(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-13worker-4

三、SpringBoot中使用虛擬線程

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

*** 注意,暫時(shí)不要用Undertow作為Servlet容器來(lái)使用虛擬線程,可能會(huì)有內(nèi)存溢出等問(wèn)題,你的內(nèi)存占用會(huì)飆得老高,SpringBoot官方目前說(shuō)的是Undertow的問(wèn)題,另外還補(bǔ)充說(shuō)如果這個(gè)問(wèn)題沒(méi)能得到解決的話,未來(lái)可能移除對(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());
    }
}

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

第二個(gè)Bean TomcatProtocolHandlerCustomizer 是為了讓Tomcat用虛擬線程來(lái)接請(qǐng)求的。我們可以測(cè)試一下,寫(xiě)一個(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ù),訪問(wèn) localhost:8080/base/test 接口的時(shí)候,會(huì)打?。?/p>

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

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

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

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

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

2、線程池問(wèn)題

如果你的打印內(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,但沒(méi)有一個(gè)名稱為“TaskExecutor”。將其中一個(gè)標(biāo)記為主任務(wù)或?qū)⑵涿麨?ldquo;taskExecutor”(可能是別名),以便將其用于異步處理:[asyncTaskExecutor,taskScheduler]
問(wèn)題是項(xiàng)目中引入的線程池過(guò)多,Spring容器不知道用哪一個(gè),我們只需要在AsyncTaskExecutor這個(gè)Bean上添加一個(gè)@Primary將我們?cè)O(shè)置的線程池設(shè)置為主線程池就行了:

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

四、總結(jié)

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

參考文獻(xiàn)

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

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

相關(guān)文章

  • Spring中的循環(huán)依賴問(wèn)題

    Spring中的循環(huán)依賴問(wèn)題

    在Spring框架中,循環(huán)依賴是指兩個(gè)或多個(gè)Bean相互依賴,這導(dǎo)致在Bean的創(chuàng)建過(guò)程中出現(xiàn)依賴死鎖,為了解決這一問(wèn)題,Spring引入了三級(jí)緩存機(jī)制,包括singletonObjects、earlySingletonObjects和singletonFactories
    2024-09-09
  • Java中的內(nèi)存模型JMM詳細(xì)解讀

    Java中的內(nèi)存模型JMM詳細(xì)解讀

    這篇文章主要介紹了Java中的內(nèi)存模型JMM詳細(xì)解讀,Java?對(duì)內(nèi)存的抽象模型如下,每個(gè)線程都有一塊自己的私有內(nèi)存(也稱為工作內(nèi)存),當(dāng)線程使用變量時(shí),會(huì)把主內(nèi)存里面的變量復(fù)制到工作內(nèi)存,線程讀寫(xiě)變量時(shí)操作的是自己工作內(nèi)存中的變量,需要的朋友可以參考下
    2023-12-12
  • Java多線程中的wait、notify和park、unpark的使用詳解

    Java多線程中的wait、notify和park、unpark的使用詳解

    這篇文章主要介紹了Java多線程中的wait、notify和park、unpark的使用詳解,它們都是線程之間進(jìn)行協(xié)作的手段,都屬于 Object 對(duì)象的方法,必須獲得此對(duì)象的鎖,才能調(diào)用這幾個(gè)方法,需要的朋友可以參考下
    2023-12-12
  • 淺析Java和Scala中的Future

    淺析Java和Scala中的Future

    這篇文章主要介紹了Java和Scala中的Future的相關(guān)資料,需要的朋友可以參考下
    2017-10-10
  • Intellij idea 代碼提示忽略字母大小寫(xiě)和常用快捷鍵及設(shè)置步驟

    Intellij idea 代碼提示忽略字母大小寫(xiě)和常用快捷鍵及設(shè)置步驟

    這篇文章主要介紹了Intellij idea 代碼提示忽略字母大小寫(xiě)和常用快捷鍵及設(shè)置步驟,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-02-02
  • 分析Java非阻塞算法Lock-Free的實(shí)現(xiàn)

    分析Java非阻塞算法Lock-Free的實(shí)現(xiàn)

    非阻塞算法一般會(huì)使用CAS來(lái)協(xié)調(diào)線程的操作。雖然非阻塞算法有諸多優(yōu)點(diǎn),但是在實(shí)現(xiàn)上要比基于鎖的算法更加繁瑣和負(fù)責(zé)。本文將會(huì)介紹兩個(gè)是用非阻塞算法實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu)。
    2021-06-06
  • 關(guān)于@Scheduled參數(shù)及cron表達(dá)式解釋

    關(guān)于@Scheduled參數(shù)及cron表達(dá)式解釋

    這篇文章主要介紹了關(guān)于@Scheduled參數(shù)及cron表達(dá)式解釋,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Flyway詳解及Springboot集成Flyway的詳細(xì)教程

    Flyway詳解及Springboot集成Flyway的詳細(xì)教程

    Flayway是一款數(shù)據(jù)庫(kù)版本控制管理工具,,支持?jǐn)?shù)據(jù)庫(kù)版本自動(dòng)升級(jí),Migrations可以寫(xiě)成sql腳本,也可以寫(xiě)在java代碼里。這篇文章主要介紹了Flyway詳解及Springboot集成Flyway的詳細(xì)教程的相關(guān)資料,需要的朋友可以參考下
    2020-07-07
  • Maven項(xiàng)目中resources配置總結(jié)

    Maven項(xiàng)目中resources配置總結(jié)

    這篇文章主要介紹了Maven項(xiàng)目中resources配置總結(jié),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • PowerJob的DesignateServer工作流程源碼解讀

    PowerJob的DesignateServer工作流程源碼解讀

    這篇文章主要介紹了PowerJob的DesignateServer工作流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01

最新評(píng)論