SpringBootスレッドプールの使用
SpringBootスレッドプールの使用
ソフトウェア環境
名前
バージョン番号
jdk
1.8
springboot
2.1.6
maven
3.3.9
1.Javaでスレッドプールを作成する
JavaミッドレンジプールのコアクラスThreadPoolExecutorのみを紹介します.その他の使い方はご自身でお問い合わせください
1.1 ThreadPoolExecutorクラスの紹介
jdk 1.8ソースコード一部削除
ThreadPoolExecutorの実行プロセスは次のとおりです.
新しいタスクの送信
YES
NO
YES
NO
YES
NO
プライマリスレッド
スレッドプール
スレッド数
新規スレッド処理要求
ワークキューがいっぱいかどうか
スレッド数
新しいタスクをキューに配置
新規スレッド処理要求
RejectedExecutionHandlerを呼び出して拒否処理を行います
jdkデフォルトでは、4つのRejectedExecutionHandlerインタフェースの実装が提供されています. AbortPolicy:RejectedExecutionException異常 を直接投げ出す CallerRunsPolicy:メインスレッドに渡す DiscardOldestPolicy:ワークキュー内の古いタスクを捨てて、新しいタスクをキューに追加します.廃棄されたタスクが再び実行できなくなる . DiscardPolicy:現在のタスクを放棄する;放棄されたタスクが再び実行できなくなる もちろん、RejectedExecutionHandlerインタフェースを実装するだけで拒否ポリシーをカスタマイズすることもできます.
2.Springでスレッドプールを作成する
2.1 ThreadPoolTaskExecutorクラスの紹介
ソースコードからThreadPoolTaskExecutorはjavaのThreadPoolExecutorに基づいてカプセル化されていることがわかります
3.スレッドプール使用例
3.1 ThreadPoolTaskExecutorの使用 pomファイル configクラス SpringBootの構成クラスでスレッドプールのBeanと対応するパラメータを構成する必要があります.呼び出しメソッドService テストクラス 試験結果
テストの結果からsayHelloメソッドが我々が定義したスレッドプール内のスレッドによって実行されていることが明らかになった.
名前の長さの制限が表示されているため、askExecutor->1が表示されますが、現在のスレッドの名前をメソッドに印刷することで、私たちが設定したスレッドthreadPoolTaskExecutorであることがわかります.>1
3.2 ThreadPoolExecutorの使用 configクラスに以下の構成 を追加する.
configクラスに2つのスレッドプールが構成されていることがわかります.いずれかのスレッドプールを使用するには、次の方法を指定します.
どのスレッドプールを使用するかが指定されていない場合は、ThreadPoolTaskExecutorが優先されます.試験結果
3.3カスタムThreadPoolTaskExecutor MyThreadPoolTaskExecutor を作成
カスタムThreadPoolTaskExecutorでは、上記のパラメータを含むスレッドプールの現在の状態を出力することができます. configクラスに構成 を追加
ThreadPoolTaskExecutorのインスタンス化されたオブジェクトをカスタムに置き換えるだけでよい
3.4@Async戻り値に基づく呼び出し
以上の例では、@Asyncの戻り値のない呼び出しに基づいています.次に、戻り値のある呼び出しについて説明します.エンティティオブジェクト を追加は、サービスにおいて、以下の方法 を追加する.
Futureの使い方に慣れていない場合は、jdkのJUCパッケージを学ぶことをお勧めします
AsyncResultはspringパッケージと@Asyncを組み合わせて使用した非同期戻り結果ですテストクラス 試験結果
3.5デフォルト構成のスレッドプールの使用
configクラスを構成する必要はありません.起動クラスに次の注釈を付けるだけです. ThreadPoolApplication service 試験方法 試験結果
ログからデフォルトで使用されているのはThreadPoolTaskExecutorというクラスであることがわかります
4実戦デモ
プロジェクトで使用したシーンを共有
ログインと顔認識の2つのサービスがあります.
ユーザーが登录する时、データベースに行ってユーザーのアカウントの状态を调べていくつか业务のロジックの判断をする必要があって、同时にオンラインの颜の识别を行う必要があります.2つの検査がすべて通る时やっと成功して登录することができます.この时私达は非同期の呼び出しのオンラインの颜の识别のインタフェースを使って、それによってシステムの応答の时间を速めることができます.
4.1コードデモ LoginService パッケージBean FaceVerifiationService
コードの中でThread.sleep()で2つの方法をシミュレートした.1つはデータベースと業務論理処理を呼び出し,1つは顔認識を呼び出すインタフェースである.
2つのメソッドが並列で、他の要因を考慮しない場合、loginメソッドの実行時間は5 s程度である.例では顔認識インタフェースを呼び出す方法を非同期で呼び出すので、最終的な実行時間は4 s程度であるべきである.試験方法 試験結果
ログからスレッドプールが呼び出されていることがわかりました.メソッドは非同期で実行され、実行時間は4018ミリ秒、つまり4秒です.
ソースアドレス:https://gitee.com/cjx940216/springboot-thread-pool-executor後記: ①
この構成はxmlでの以前の構成と同じです
②ここで使われているのは
できません
Spring-aopはエージェントによってメソッドを強化するため(拡張メソッドの注釈は@Transactionalや@Asynなど)、ここでt h i s.bfcolor{purple}{this.}this.g e t O n l i n e F a c e V e r i f i f i c a t i o n e s u l tbfcolor{blue}{getOnlineFaceVerificationResult}getOnlineFaceVerificationResult(u s e r I n f o)\bf\color{black}{(userInfo)} (userInfo);エージェントではなく真実の方法を使用しているので、非同期操作は実行されません.つまりspring-aopの強化を迂回して、具体的な詳細は次の文章で説明します.
ソフトウェア環境
名前
バージョン番号
jdk
1.8
springboot
2.1.6
maven
3.3.9
1.Javaでスレッドプールを作成する
JavaミッドレンジプールのコアクラスThreadPoolExecutorのみを紹介します.その他の使い方はご自身でお問い合わせください
1.1 ThreadPoolExecutorクラスの紹介
jdk 1.8ソースコード一部削除
package java.util.concurrent;
/**
* @param corePoolSize -> , ,
* {@code allowCoreThreadTimeOut}
* @param maximumPoolSize ->
* , OOM(OutOfMemoryError)
* @param keepAliveTime -> ,
* , .
* @param unit {@code keepAliveTime} .eg:TimeUnit.SECONDS
* @param workQueue -> .
* @param threadFactory -> .
* ( ThreadFactory , 、
* , .
* , ThreadFactory 。)
* @param handler , handler,
* ( ThreadPoolExecutor execute )
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
ThreadPoolExecutorの実行プロセスは次のとおりです.
新しいタスクの送信
YES
NO
YES
NO
YES
NO
プライマリスレッド
スレッドプール
スレッド数
新規スレッド処理要求
ワークキューがいっぱいかどうか
スレッド数
新しいタスクをキューに配置
新規スレッド処理要求
RejectedExecutionHandlerを呼び出して拒否処理を行います
1.
2. , ; .
3. ; .
4. ; RejectedExecutionHandler .
jdkデフォルトでは、4つのRejectedExecutionHandlerインタフェースの実装が提供されています.
2.Springでスレッドプールを作成する
2.1 ThreadPoolTaskExecutorクラスの紹介
package org.springframework.scheduling.concurrent;
public class ThreadPoolTaskExecutor {
private final Object poolSizeMonitor = new Object(); // ,
private int corePoolSize = 1; //
private int maxPoolSize = 2147483647; //
private int keepAliveSeconds = 60; //
private int queueCapacity = 2147483647; //
private boolean allowCoreThreadTimeOut = false; // ,false
private TaskDecorator taskDecorator; // , /
private ThreadPoolExecutor threadPoolExecutor; // java
}
ソースコードからThreadPoolTaskExecutorはjavaのThreadPoolExecutorに基づいてカプセル化されていることがわかります
3.スレッドプール使用例
3.1 ThreadPoolTaskExecutorの使用
<!-- springboot-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- springboot-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
@EnableAsync //
public class ThreadPoolConfig {
@Bean
public Executor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
//
threadPoolTaskExecutor.setCorePoolSize(5);
//
threadPoolTaskExecutor.setMaxPoolSize(5);
//
threadPoolTaskExecutor.setQueueCapacity(2000);
//
threadPoolTaskExecutor.setThreadNamePrefix("threadPoolTaskExecutor-->");
// . , , RejectedExecutionException
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
//
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class HelloService {
Logger logger = LoggerFactory.getLogger(HelloService.class);
/**
* @Async , ; ,
* , , 。
*/
@Async //
public void sayHello() {
logger.info("start say hello");
System.out.println(Thread.currentThread().getName());
System.out.println("hello");
logger.info("end say hello");
}
}
import com.cain.threadpool.ThreadPoolApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ThreadPoolApplication.class)
public class HelloServiceTest {
@Autowired
HelloService helloService;
@Test
public void testSayHello() throws Exception {
helloService.sayHello();
}
}
2019-07-02 18:36:25.138 INFO 2868 --- [ main] t.c.e.demo.service.HelloServiceTest : Starting HelloServiceTest on DLC00R90RK7NBL with PID 2868 (started by jiaxin.chi in C:\Users\jiaxin.chi\Desktop\demo)
2019-07-02 18:36:25.140 INFO 2868 --- [ main] t.c.e.demo.service.HelloServiceTest : No active profile set, falling back to default profiles: default
2019-07-02 18:36:26.892 INFO 2868 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService
2019-07-02 18:36:26.913 INFO 2868 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'threadPoolTaskExecutor'
2019-07-02 18:36:28.644 INFO 2868 --- [ main] t.c.e.demo.service.HelloServiceTest : Started HelloServiceTest in 3.98 seconds (JVM running for 6.103)
2019-07-02 18:36:29.047 INFO 2868 --- [askExecutor-->1] com.example.demo.service.HelloService : start say hello
threadPoolTaskExecutor-->1
hello
2019-07-02 18:36:29.048 INFO 2868 --- [askExecutor-->1] com.example.demo.service.HelloService : end say hello
2019-07-02 18:36:29.051 INFO 2868 --- [ Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'threadPoolTaskExecutor'
テストの結果からsayHelloメソッドが我々が定義したスレッドプール内のスレッドによって実行されていることが明らかになった.
名前の長さの制限が表示されているため、askExecutor->1が表示されますが、現在のスレッドの名前をメソッドに印刷することで、私たちが設定したスレッドthreadPoolTaskExecutorであることがわかります.>1
3.2 ThreadPoolExecutorの使用
@Bean
public Executor myThreadPool() {
//
int corePoolSize = 5;
//
int maxPoolSize = 5;
//
int queueCapacity = 2000;
//
long keepAliveTime = 30;
//
String threadNamePrefix = "myThreadPool-->";
// . , , RejectedExecutionException
RejectedExecutionHandler rejectedExecutionHandler = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
throw new RejectedExecutionException(" RejectedExecutionHandler");
}
};
//
ThreadFactory threadFactory = new ThreadFactory() {
private int i = 1;
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName(threadNamePrefix + i);
i++;
return thread;
}
};
//
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maxPoolSize,
keepAliveTime, TimeUnit.SECONDS, new LinkedBlockingQueue<>(queueCapacity),
threadFactory, rejectedExecutionHandler);
return threadPoolExecutor;
}
configクラスに2つのスレッドプールが構成されていることがわかります.いずれかのスレッドプールを使用するには、次の方法を指定します.
どのスレッドプールを使用するかが指定されていない場合は、ThreadPoolTaskExecutorが優先されます.
@Async("myThreadPool") // bean id ①
public void sayHello() {
logger.info("start say hello");
System.out.println(Thread.currentThread().getName());
System.out.println("hello");
logger.info("end say hello");
}
2019-07-03 10:26:55.515 INFO 13304 --- [ main] t.c.e.demo.service.HelloServiceTest : Starting HelloServiceTest on DLC00R90RK7NBL with PID 13304 (started by jiaxin.chi in C:\Users\jiaxin.chi\Desktop\demo)
2019-07-03 10:26:55.517 INFO 13304 --- [ main] t.c.e.demo.service.HelloServiceTest : No active profile set, falling back to default profiles: default
2019-07-03 10:26:56.768 INFO 13304 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService
2019-07-03 10:26:56.789 INFO 13304 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'threadPoolTaskExecutor'
2019-07-03 10:26:57.997 INFO 13304 --- [ main] t.c.e.demo.service.HelloServiceTest : Started HelloServiceTest in 2.824 seconds (JVM running for 4.462)
2019-07-03 10:26:58.258 INFO 13304 --- [yThreadPool-->1] com.example.demo.service.HelloService : start say hello
myThreadPool-->1
hello
2019-07-03 10:26:58.258 INFO 13304 --- [yThreadPool-->1] com.example.demo.service.HelloService : end say hello
2019-07-03 10:26:58.260 INFO 13304 --- [ Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'threadPoolTaskExecutor'
3.3カスタムThreadPoolTaskExecutor
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.concurrent.ListenableFuture;
public class MyThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
Logger logger = LoggerFactory.getLogger(MyThreadPoolTaskExecutor.class);
@Override
public void execute(Runnable task) {
logThreadPoolStatus();
super.execute(task);
}
@Override
public void execute(Runnable task, long startTimeout) {
logThreadPoolStatus();
super.execute(task, startTimeout);
}
@Override
public Future<?> submit(Runnable task) {
logThreadPoolStatus();
return super.submit(task);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
logThreadPoolStatus();
return super.submit(task);
}
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
logThreadPoolStatus();
return super.submitListenable(task);
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
logThreadPoolStatus();
return super.submitListenable(task);
}
/**
*
*/
private void logThreadPoolStatus() {
logger.info(" :{}, :{}, : {}, : {}",
getCorePoolSize(), getMaxPoolSize(), getPoolSize(), getActiveCount());
}
}
カスタムThreadPoolTaskExecutorでは、上記のパラメータを含むスレッドプールの現在の状態を出力することができます.
@Bean
public Executor myThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new MyThreadPoolTaskExecutor();
//
threadPoolTaskExecutor.setCorePoolSize(5);
//
threadPoolTaskExecutor.setMaxPoolSize(5);
//
threadPoolTaskExecutor.setQueueCapacity(2000);
//
threadPoolTaskExecutor.setThreadNamePrefix("myThreadPoolTaskExecutor-->");
// . , , RejectedExecutionException
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
//
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
ThreadPoolTaskExecutorのインスタンス化されたオブジェクトをカスタムに置き換えるだけでよい
3.4@Async戻り値に基づく呼び出し
以上の例では、@Asyncの戻り値のない呼び出しに基づいています.次に、戻り値のある呼び出しについて説明します.
public class HelloEntity {
private String helloStr;
public String getHelloStr() {
return helloStr;
}
public void setHelloStr(String helloStr) {
this.helloStr = helloStr;
}
}
@Async
public Future<HelloEntity> getHelloString() {
logger.info("start getHelloString");
HelloEntity helloEntity = new HelloEntity();
helloEntity.setHelloStr("Say hello to little wang");
System.out.println(Thread.currentThread().getName());
logger.info("end getHelloString");
return new AsyncResult<>(helloEntity);
}
Futureの使い方に慣れていない場合は、jdkのJUCパッケージを学ぶことをお勧めします
AsyncResultはspringパッケージと@Asyncを組み合わせて使用した非同期戻り結果です
Tips: : , , 。
, AsyncTaskExecutor。
AsyncResult
[As of Spring 4.2, this class also supports passing execution exceptions back to the caller.]
Spring4.2 , .
spring .
, .
@Test
public void testGetHelloString() throws Exception {
Future<HelloEntity> helloString = helloService.getHelloString();
HelloEntity helloEntity = helloString.get();
System.out.println(helloEntity.getHelloStr());
}
2019-07-03 11:01:11.603 INFO 13492 --- [ main] t.c.e.demo.service.HelloServiceTest : Starting HelloServiceTest on DLC00R90RK7NBL with PID 13492 (started by jiaxin.chi in C:\Users\jiaxin.chi\Desktop\demo)
2019-07-03 11:01:11.604 INFO 13492 --- [ main] t.c.e.demo.service.HelloServiceTest : No active profile set, falling back to default profiles: default
2019-07-03 11:01:13.205 INFO 13492 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService
2019-07-03 11:01:13.226 INFO 13492 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'threadPoolTaskExecutor'
2019-07-03 11:01:14.729 INFO 13492 --- [ main] t.c.e.demo.service.HelloServiceTest : Started HelloServiceTest in 3.613 seconds (JVM running for 5.579)
2019-07-03 11:01:15.070 INFO 13492 --- [askExecutor-->1] com.example.demo.service.HelloService : start getHelloString
threadPoolTaskExecutor-->1
2019-07-03 11:01:15.071 INFO 13492 --- [askExecutor-->1] com.example.demo.service.HelloService : end getHelloString
Say hello to little wang
2019-07-03 11:01:15.087 INFO 13492 --- [ Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'threadPoolTaskExecutor'
3.5デフォルト構成のスレッドプールの使用
configクラスを構成する必要はありません.起動クラスに次の注釈を付けるだけです.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync //
public class ThreadPoolApplication {
public static void main(String[] args) {
SpringApplication.run(ThreadPoolApplication.class, args);
}
}
@Async
public void sayHello() {
logger.info("start say hello");
System.out.println(Thread.currentThread().getName());
System.out.println("hello");
logger.info("end say hello");
}
@Test
public void testSayHello() throws Exception {
helloService.sayHello();
}
2019-07-04 17:26:57.406 INFO 20620 --- [ main] c.c.threadpool.service.HelloServiceTest : Starting HelloServiceTest on DLC00R90RK7NBL with PID 20620 (started by jiaxin.chi in E:\threadpooltest)
2019-07-04 17:26:57.407 INFO 20620 --- [ main] c.c.threadpool.service.HelloServiceTest : No active profile set, falling back to default profiles: default
2019-07-04 17:26:59.757 INFO 20620 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-07-04 17:27:00.458 INFO 20620 --- [ main] c.c.threadpool.service.HelloServiceTest : Started HelloServiceTest in 3.337 seconds (JVM running for 4.728)
2019-07-04 17:27:00.880 INFO 20620 --- [ task-1] c.cain.threadpool.service.HelloService : start say hello
task-1
hello
2019-07-04 17:27:00.880 INFO 20620 --- [ task-1] c.cain.threadpool.service.HelloService : end say hello
2019-07-04 17:27:00.887 INFO 20620 --- [ Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
Disconnected from the target VM, address: '127.0.0.1:49464', transport: 'socket'
ログからデフォルトで使用されているのはThreadPoolTaskExecutorというクラスであることがわかります
Tips: ,
4実戦デモ
プロジェクトで使用したシーンを共有
ログインと顔認識の2つのサービスがあります.
ユーザーが登录する时、データベースに行ってユーザーのアカウントの状态を调べていくつか业务のロジックの判断をする必要があって、同时にオンラインの颜の识别を行う必要があります.2つの検査がすべて通る时やっと成功して登录することができます.この时私达は非同期の呼び出しのオンラインの颜の识别のインタフェースを使って、それによってシステムの応答の时间を速めることができます.
4.1コードデモ
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import com.cain.threadpool.entity.CainResult;
import com.cain.threadpool.entity.FaceVerificationResult;
import com.cain.threadpool.entity.UserInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
@Service
public class LoginService {
Logger logger = LoggerFactory.getLogger(LoginService.class);
@Autowired
FaceVerifiationService faceVerifiationService;
@Autowired
ApplicationContext applicationContext; ②
public CainResult login(UserInfo userInfo) {
CainResult cainResult = new CainResult();
LoginService loginService = applicationContext.getBean(LoginService.class);
Future<FaceVerificationResult> onlineFaceVerificationResult = loginService.getOnlineFaceVerificationResult(userInfo);
try {
//
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
FaceVerificationResult faceVerificationResult = null;
try {
// 3
faceVerificationResult = onlineFaceVerificationResult.get(3, TimeUnit.SECONDS);
} catch (Exception e) {
logger.error(" ");
//
}
if (faceVerificationResult != null) {
cainResult.setCode(faceVerificationResult.getCode());
cainResult.setMessage(faceVerificationResult.getMessage());
}
return cainResult;
}
@Async
public Future<FaceVerificationResult> getOnlineFaceVerificationResult(UserInfo userInfo) {
logger.info(" ");
FaceVerificationResult result = faceVerifiationService.getResult(userInfo);
return new AsyncResult<>(result);
}
}
public class CainResult {
private Integer code;
private String message;
}
public class FaceVerificationResult {
private int code;
private String message;
}
public class UserInfo {
private Integer id;
private String name;
private String photo;
}
package com.cain.threadpool.service;
import com.cain.threadpool.entity.FaceVerificationResult;
import com.cain.threadpool.entity.UserInfo;
import org.springframework.stereotype.Service;
@Service
public class FaceVerifiationService {
public FaceVerificationResult getResult(UserInfo userInfo) {
FaceVerificationResult faceVerificationResult = new FaceVerificationResult();
// 100
faceVerificationResult.setCode(100);
faceVerificationResult.setMessage("success");
try {
// http
Thread.sleep(1000);
//
// 4( ) + 3( ) 7s
} catch (InterruptedException e) {
e.printStackTrace();
}
return faceVerificationResult;
}
}
コードの中でThread.sleep()で2つの方法をシミュレートした.1つはデータベースと業務論理処理を呼び出し,1つは顔認識を呼び出すインタフェースである.
2つのメソッドが並列で、他の要因を考慮しない場合、loginメソッドの実行時間は5 s程度である.例では顔認識インタフェースを呼び出す方法を非同期で呼び出すので、最終的な実行時間は4 s程度であるべきである.
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ThreadPoolApplication.class)
public class LoginServiceTest {
@Autowired
LoginService loginService;
@Test
public void login() {
UserInfo userInfo = new UserInfo();
long start = Instant.now().toEpochMilli();
loginService.login(userInfo);
long end = Instant.now().toEpochMilli();
System.out.println(end - start);
}
}
2019-07-04 18:51:36.968 INFO 28924 --- [ main] c.c.threadpool.service.LoginServiceTest : Starting LoginServiceTest on DLC00R90RK7NBL with PID 28924 (started by jiaxin.chi in E:\threadpooltest)
2019-07-04 18:51:36.970 INFO 28924 --- [ main] c.c.threadpool.service.LoginServiceTest : No active profile set, falling back to default profiles: default
2019-07-04 18:51:39.622 INFO 28924 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-07-04 18:51:40.143 INFO 28924 --- [ main] c.c.threadpool.service.LoginServiceTest : Started LoginServiceTest in 3.728 seconds (JVM running for 6.121)
2019-07-04 18:51:40.505 INFO 28924 --- [ task-1] c.cain.threadpool.service.LoginService :
4018
2019-07-04 18:51:44.514 INFO 28924 --- [ Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
ログからスレッドプールが呼び出されていることがわかりました.メソッドは非同期で実行され、実行時間は4018ミリ秒、つまり4秒です.
ソースアドレス:https://gitee.com/cjx940216/springboot-thread-pool-executor
@Configuration
public class ThreadPoolConfig {
@Bean
public HelloService helloService() {
...
}
}
この構成はxmlでの以前の構成と同じです
<beans>
<bean id="helloService" class="com.cain.HelloServiceImpl"/>
</beans>
②ここで使われているのは
LoginService loginService = applicationContext.getBean(LoginService.class);
loginService.getOnlineFaceVerificationResult(userInfo);
できません
this.getOnlineFaceVerificationResult(userInfo);
Spring-aopはエージェントによってメソッドを強化するため(拡張メソッドの注釈は@Transactionalや@Asynなど)、ここでt h i s.bfcolor{purple}{this.}this.g e t O n l i n e F a c e V e r i f i f i c a t i o n e s u l tbfcolor{blue}{getOnlineFaceVerificationResult}getOnlineFaceVerificationResult(u s e r I n f o)\bf\color{black}{(userInfo)} (userInfo);エージェントではなく真実の方法を使用しているので、非同期操作は実行されません.つまりspring-aopの強化を迂回して、具体的な詳細は次の文章で説明します.