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

詳解Spring/Spring boot異步任務(wù)編程WebAsyncTask

 更新時(shí)間:2018年06月18日 11:40:40   作者:明人不說暗話___我喜歡你  
這篇文章主要介紹了詳解Spring/Spring boot異步任務(wù)編程WebAsyncTask,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

今天一起學(xué)習(xí)下如何在Spring中進(jìn)行異步編程。我們都知道,web服務(wù)器處理請(qǐng)求 request 的線程是從線程池中獲取的,這也不難解釋,因?yàn)楫?dāng)web請(qǐng)求并發(fā)數(shù)非常大時(shí),如何一個(gè)請(qǐng)求進(jìn)來就創(chuàng)建一條處理線程,由于創(chuàng)建線程和線程上下文切換的開銷是比較大的,web服務(wù)器最終將面臨崩潰。另外,web服務(wù)器創(chuàng)建的處理線程從頭到尾默認(rèn)是同步執(zhí)行的,也就是說,假如處理線程A負(fù)責(zé)處理請(qǐng)求B,那么當(dāng)B沒有 return 之前,處理線程A是不可以脫身去處理別的請(qǐng)求的,這將極大限制了web服務(wù)器的并發(fā)處理能力。

因此線程池解決了線程可循環(huán)利用的問題,那同步處理請(qǐng)求怎么去解決呢?答案是異步處理。什么是異步處理呢?異步處理主要是讓上面的B請(qǐng)求處理完成之前,能夠?qū)線程空閑出來繼續(xù)去處理別的請(qǐng)求。那么我們可以這樣做,在A線程內(nèi)部重新開啟一個(gè)線程C去執(zhí)行任務(wù),讓A直接返回給web服務(wù)器,繼續(xù)接受新進(jìn)來的請(qǐng)求。

在開始下面的講解之前,我在這里先區(qū)別下兩個(gè)概念:

1、處理線程

處理線程屬于web服務(wù)器,負(fù)責(zé)處理用戶請(qǐng)求,采用線程池管理

2、異步線程

異步線程屬于用戶自定義的線程,可采用線程池管理

spring中提供了對(duì)異步任務(wù)的支持,采用 WebAsyncTask 類即可實(shí)現(xiàn)異步任務(wù),同時(shí)我們也可以對(duì)異步任務(wù)設(shè)置相應(yīng)的回調(diào)處理,如當(dāng)任務(wù)超時(shí)、拋出異常怎么處理等。異步任務(wù)通常非常實(shí)用,比如我們想讓一個(gè)可能會(huì)處理很長(zhǎng)時(shí)間的操作交給異步線程去處理,又或者當(dāng)一筆訂單支付完成之后,開啟異步任務(wù)查詢訂單的支付結(jié)果。

一、正常異步任務(wù)

為了演示方便,異步任務(wù)的執(zhí)行采用 Thread.sleep(long) 模擬,現(xiàn)在假設(shè)用戶請(qǐng)求以下接口 :

http://localhost:7000/demo/getUserWithNoThing.json

異步任務(wù)接口定義如下:

/**
 * 測(cè)試沒有發(fā)生任何異常的異步任務(wù)
 */
@RequestMapping(value = "getUserWithNoThing.json", method = RequestMethod.GET)
public WebAsyncTask<String> getUserWithNoThing() {
 // 打印處理線程名
 System.err.println("The main Thread name is " + Thread.currentThread().getName());
 
 // 此處模擬開啟一個(gè)異步任務(wù),超時(shí)時(shí)間為10s
 WebAsyncTask<String> task1 = new WebAsyncTask<String>(10 * 1000L, () -> {
 	System.err.println("The first Thread name is " + Thread.currentThread().getName());
 	// 任務(wù)處理時(shí)間5s,不超時(shí)
 	Thread.sleep(5 * 1000L);
 	return "任務(wù)1順利執(zhí)行成功!任何異常都沒有拋出!";
 });
 
 // 任務(wù)執(zhí)行完成時(shí)調(diào)用該方法
 task1.onCompletion(() -> {
 	System.err.println("任務(wù)1執(zhí)行完成啦!");
 });
 
 System.err.println("task1繼續(xù)處理其他事情!");
 return task1;
}

控制臺(tái)打印如下:

The main Thread name is http-nio-7000-exec-1
task1繼續(xù)處理其他事情!
The first Thread name is MvcAsync1
任務(wù)1執(zhí)行完成啦!

瀏覽器結(jié)果如下:

 

二、拋異常異步任務(wù)

接口調(diào)用 : http://localhost:7000/demo/getUserWithError.json

/**
 * 測(cè)試發(fā)生error的異步任務(wù)
 * @return
 */
@RequestMapping(value = "getUserWithError.json", method = RequestMethod.GET)
public WebAsyncTask<String> getUserWithError() {
	System.err.println("The main Thread name is " + Thread.currentThread().getName());

	// 此處模擬開啟一個(gè)異步任務(wù)
	WebAsyncTask<String> task3 = new WebAsyncTask<String>(10 * 1000L, () -> {
		System.err.println("The second Thread name is " + Thread.currentThread().getName());
		// 此處拋出異常
		int num = 9 / 0;
		System.err.println(num);
		return "";
	});

	// 發(fā)生異常時(shí)調(diào)用該方法
	task3.onError(() -> {
		System.err.println("====================================" + Thread.currentThread().getName()
				+ "==============================");
		System.err.println("任務(wù)3發(fā)生error啦!");
		return "";
	});
	// 任務(wù)執(zhí)行完成時(shí)調(diào)用該方法
	task3.onCompletion(() -> {
		System.err.println("任務(wù)3執(zhí)行完成啦!");
	});

	System.err.println("task3繼續(xù)處理其他事情!");
	return task3;
}

控制臺(tái)輸出如下:

The main Thread name is http-nio-7000-exec-1
task3繼續(xù)處理其他事情!
The second Thread name is MvcAsync1
2018-06-15 09:40:13.538 ERROR 9168 --- [nio-7000-exec-2] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] threw exception

java.lang.ArithmeticException: / by zero
at com.example.demo.controller.GetUserInfoController.lambda$5(GetUserInfoController.java:93) ~[classes/:na]
at org.springframework.web.context.request.async.WebAsyncManager.lambda$startCallableProcessing$4(WebAsyncManager.java:317) ~[spring-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[na:1.8.0_161]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_161]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_161]

2018-06-15 09:40:13.539 ERROR 9168 --- [nio-7000-exec-2] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [/demo] threw exception [Request processing failed; nested exception is java.lang.ArithmeticException: / by zero] with root cause

java.lang.ArithmeticException: / by zero
at com.example.demo.controller.GetUserInfoController.lambda$5(GetUserInfoController.java:93) ~[classes/:na]
at org.springframework.web.context.request.async.WebAsyncManager.lambda$startCallableProcessing$4(WebAsyncManager.java:317) ~[spring-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[na:1.8.0_161]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_161]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_161]

====================================http-nio-7000-exec-2==============================
任務(wù)3發(fā)生error啦!
任務(wù)3執(zhí)行完成啦!

當(dāng)然你也可以對(duì)上面做一些異常處理,不至于在用戶看來顯得不友好,關(guān)于異常處理,可以查看我的另一篇文章 Spring boot/Spring 統(tǒng)一錯(cuò)誤處理方案的使用

瀏覽器輸出結(jié)果:

 

三、超時(shí)異步任務(wù)

接口調(diào)用 : http://localhost:7000/demo/getUserWithTimeOut.json

/**
 * 測(cè)試發(fā)生任務(wù)超時(shí)的異步任務(wù)
 * @return
 */
@RequestMapping(value = "getUserWithTimeOut.json", method = RequestMethod.GET)
public WebAsyncTask<String> getUserWithTimeOut() {
 System.err.println("The main Thread name is " + Thread.currentThread().getName());
 
 // 此處模擬開啟一個(gè)異步任務(wù),超時(shí)10s
 WebAsyncTask<String> task2 = new WebAsyncTask<String>(10 * 1000L, () -> {
 	System.err.println("The second Thread name is " + Thread.currentThread().getName());
 	Thread.sleep(20 * 1000L);
 	return "任務(wù)2執(zhí)行超時(shí)!";
 });
 
 // 任務(wù)超時(shí)調(diào)用該方法
 task2.onTimeout(() -> {
 	System.err.println("====================================" + Thread.currentThread().getName()
 			+ "==============================");
 	return "任務(wù)2發(fā)生超時(shí)啦!";
 });
 
 // 任務(wù)執(zhí)行完成時(shí)調(diào)用該方法
 task2.onCompletion(() -> {
 	System.err.println("任務(wù)2執(zhí)行完成啦!");
 });
 
 System.err.println("task2繼續(xù)處理其他事情!");
 return task2;
}

控制臺(tái)執(zhí)行結(jié)果:

The main Thread name is http-nio-7000-exec-4
task2繼續(xù)處理其他事情!
The second Thread name is MvcAsync2
====================================http-nio-7000-exec-5==============================
任務(wù)2執(zhí)行完成啦!

瀏覽器執(zhí)行結(jié)果:

 

四、線程池異步任務(wù)

上面的三種情況中的異步任務(wù)默認(rèn)不是采用線程池機(jī)制進(jìn)行管理的,也就是說,一個(gè)請(qǐng)求進(jìn)來,雖然釋放了處理線程,但是系統(tǒng)依舊會(huì)為每個(gè)請(qǐng)求創(chuàng)建一個(gè)異步任務(wù)線程,也就是上面我們看到的 MvcAsync 開頭的異步任務(wù)線程,那這樣不行啊,開銷特別大呀!所以我們可以采用線程池進(jìn)行管理,直接在 WebAsyncTask 類構(gòu)造器傳入一個(gè) ThreadPoolTaskExecutor 對(duì)象實(shí)例即可。

下面我們先看看,當(dāng)對(duì)上面第一種情況執(zhí)行并發(fā)請(qǐng)求時(shí)會(huì)出現(xiàn)什么情況(此處模擬對(duì) http://localhost:7000/demo/getUserWithNoThing.json 進(jìn)行并發(fā)調(diào)用):

控制臺(tái)輸出如下:

The first Thread name is MvcAsync57
The first Thread name is MvcAsync58
The first Thread name is MvcAsync59
The first Thread name is MvcAsync60
The first Thread name is MvcAsync61
The first Thread name is MvcAsync62
The first Thread name is MvcAsync63
The first Thread name is MvcAsync64
The first Thread name is MvcAsync65
The first Thread name is MvcAsync66
The first Thread name is MvcAsync67
The first Thread name is MvcAsync68
The first Thread name is MvcAsync69
The first Thread name is MvcAsync70
The first Thread name is MvcAsync71
The first Thread name is MvcAsync72
The first Thread name is MvcAsync73
The first Thread name is MvcAsync74
The first Thread name is MvcAsync76
The first Thread name is MvcAsync75
The first Thread name is MvcAsync77
The first Thread name is MvcAsync78
The first Thread name is MvcAsync79
The first Thread name is MvcAsync80

由于沒有加入線程池,所以100個(gè)請(qǐng)求將開啟100個(gè)異步任務(wù)線程,開銷特別大,不推薦。

下面是采用線程池的實(shí)現(xiàn) :

調(diào)用接口 : http://localhost:7000/demo/getUserWithExecutor.json

/**
 * 測(cè)試線程池
 * @return
 */
@RequestMapping(value = "getUserWithExecutor.json", method = RequestMethod.GET)
public WebAsyncTask<String> getUserWithExecutor() {
 System.err.println("The main Thread name is " + Thread.currentThread().getName());
 
 // 此處模擬開啟一個(gè)異步任務(wù),此處傳入一個(gè)線程池
 WebAsyncTask<String> task1 = new WebAsyncTask<String>(10 * 1000L, executor, () -> {
 	System.err.println("The first Thread name is " + Thread.currentThread().getName());
 	Thread.sleep(5000L);
 	return "任務(wù)4順利執(zhí)行成功!任何異常都沒有拋出!";
 });
 
 // 任務(wù)執(zhí)行完成時(shí)調(diào)用該方法
 task1.onCompletion(() -> {
 	System.err.println("任務(wù)4執(zhí)行完成啦!");
 });
 
 System.err.println("task4繼續(xù)處理其他事情!");
 return task1;
}

線程池定義如下:

@Configuration
public class MyExecutor {
 
 @Bean
 public static ThreadPoolTaskExecutor getExecutor() {
 	ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
 	taskExecutor.setCorePoolSize(30);
 	taskExecutor.setMaxPoolSize(30);
 	taskExecutor.setQueueCapacity(50);
 	taskExecutor.setThreadNamePrefix("huang");// 異步任務(wù)線程名以 huang 為前綴
 	return taskExecutor;
 }
}

對(duì)上面進(jìn)行并發(fā)測(cè)試,可以得出下面結(jié)果 :

本文示例代碼地址: https://github.com/SmallerCoder/WebAsyncTask

采用線程池可以節(jié)約服務(wù)器資源,優(yōu)化服務(wù)器處理能力,要記得常用喲!謝謝閱讀!覺得對(duì)你有幫助,請(qǐng)給個(gè)start哦!

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 如何實(shí)現(xiàn)Java中一個(gè)簡(jiǎn)單的LinkedList

    如何實(shí)現(xiàn)Java中一個(gè)簡(jiǎn)單的LinkedList

    LinkedList與ArrayList都是List接口的具體實(shí)現(xiàn)類。下面將介紹如何實(shí)現(xiàn)一個(gè)簡(jiǎn)單的LinkedList,具有很好的參考價(jià)值,下面跟著小編一起來看下吧
    2017-02-02
  • Java實(shí)現(xiàn)自定義自旋鎖代碼實(shí)例

    Java實(shí)現(xiàn)自定義自旋鎖代碼實(shí)例

    這篇文章主要介紹了Java實(shí)現(xiàn)自定義自旋鎖代碼實(shí)例,Java自旋鎖是一種線程同步機(jī)制,它允許線程在獲取鎖時(shí)不立即阻塞,而是通過循環(huán)不斷嘗試獲取鎖,直到成功獲取為止,自旋鎖適用于鎖競(jìng)爭(zhēng)激烈但持有鎖的時(shí)間很短的情況,需要的朋友可以參考下
    2023-10-10
  • 關(guān)于JSCH使用自定義連接池的說明

    關(guān)于JSCH使用自定義連接池的說明

    這篇文章主要介紹了關(guān)于JSCH使用自定義連接池的說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • SpringBoot+TestNG單元測(cè)試的實(shí)現(xiàn)

    SpringBoot+TestNG單元測(cè)試的實(shí)現(xiàn)

    本文主要介紹了SpringBoot+TestNG單元測(cè)試的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-07-07
  • java  中MyBatis注解映射的實(shí)例詳解

    java 中MyBatis注解映射的實(shí)例詳解

    這篇文章主要介紹了java 中MyBatis注解映射的實(shí)例詳解的相關(guān)資料,這里提供實(shí)例幫助大家理解這部分內(nèi)容,需要的朋友可以參考下
    2017-09-09
  • Component和Configuration注解區(qū)別實(shí)例詳解

    Component和Configuration注解區(qū)別實(shí)例詳解

    這篇文章主要為大家介紹了Component和Configuration注解區(qū)別實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • fastJson泛型如何轉(zhuǎn)換的實(shí)現(xiàn)

    fastJson泛型如何轉(zhuǎn)換的實(shí)現(xiàn)

    這篇文章主要介紹了fastJson泛型如何轉(zhuǎn)換的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • Spring?Boot中使用Spring?Retry重試框架的操作方法

    Spring?Boot中使用Spring?Retry重試框架的操作方法

    這篇文章主要介紹了Spring?Retry?在SpringBoot?中的應(yīng)用,介紹了RetryTemplate配置的時(shí)候,需要設(shè)置的重試策略和退避策略,需要的朋友可以參考下
    2022-04-04
  • Spring核心容器IOC原理實(shí)例解析

    Spring核心容器IOC原理實(shí)例解析

    這篇文章主要介紹了Spring核心容器IOC原理實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • SpringBoot Mybatis Plus公共字段自動(dòng)填充功能

    SpringBoot Mybatis Plus公共字段自動(dòng)填充功能

    這篇文章主要介紹了SpringBoot Mybatis Plus公共字段自動(dòng)填充功能的相關(guān)資料,需要的朋友可以參考下
    2017-04-04

最新評(píng)論

丘北县| 青海省| 重庆市| 类乌齐县| 泸州市| 上饶县| 延边| 栾川县| 盐池县| 南昌县| 莆田市| 温宿县| 黔江区| 凌源市| 钟山县| 和政县| 宁阳县| 龙州县| 双桥区| 乐山市| 三门峡市| 宝兴县| 宜昌市| 益阳市| 布拖县| 兴义市| 永德县| 双桥区| 湖南省| 龙南县| 晋江市| 临汾市| 大荔县| 五指山市| 分宜县| 崇义县| 汽车| 凭祥市| 衢州市| 广西| 剑阁县|