Spring 引导@Async 与@PostConstruct 一起使用
Spring Boot @Async use With @PostConstruct
编辑解决方案
这最终成为我的 @Service
class 多线程工作。它同时填充数据库中的每个 table 。此解决方案不再需要 @Async
注释或任务执行器。
@Service
public class AsyncDBLoad extends Thread {
final CovidDataServices service;
Thread.UncaughtExceptionHandler exceptionHandler = new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread th, Throwable ex) {
System.out.println("Uncaught exception: " + ex.getMessage()
+ "\nIn Thread: " + th.getName());
}
};
public AsyncDBLoad(CovidDataServices service) {
this.service = service;
}
@PostConstruct
public void initializeAsyncDB() throws IOException, InterruptedException {
// Thread to populate state DB
Thread stateThread = new Thread() {
@Override
public void run() {
try {
service.populateDbWithStateData();
} catch (InterruptedException | IOException e) {
System.out.println("State thread error");
}
}
};
// Thread to populate country DB
Thread countryThread = new Thread() {
@Override
public void run() {
try {
service.populateDBWithCountryData();
} catch (InterruptedException | IOException e) {
System.out.println("Country thread error");
}
}
};
// set error handlers
stateThread.setUncaughtExceptionHandler(exceptionHandler);
countryThread.setUncaughtExceptionHandler(exceptionHandler);
// set names
stateThread.setName("State Thread");
countryThread.setName("Country Thread");
// start the threads
stateThread.start();
countryThread.start();
}
}
原问题
我正在尝试在 @Service
中使用 2 @PostConstruct
方法与 @Async
一起使用。
特别是在启动时,我正在填充数据库 table,它们彼此分开,可以同时加载。
我尝试按照 here 并异步获得第一个 @PostConstruct
到 运行 但第二个方法仍然在等待第一个完成之前开始。
这是我目前拥有的代码。
应用程序开始:
@SpringBootApplication
@EnableAsync
public class Covid19Trackerv2Application {
public static void main(String[] args) {
SpringApplication.run(Covid19Trackerv2Application.class, args);
}
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("GithubLookup-");
executor.initialize();
return executor;
}
}
@Service
调用 @Async
方法:
@Service
public class AsyncDBLoad {
final CovidDataServices service;
public AsyncDBLoad(CovidDataServices service) {
this.service = service;
}
@PostConstruct
public void initializeAsyncDB() throws IOException, InterruptedException {
service.populateDBWithCountryData();
}
@PostConstruct
public void initializeAsyncDB2() throws IOException, InterruptedException {
service.populateDbWithStateData();
}
}
持有@Async
方法的CovidDataServices@Service
的相关部分:
@Async
public void populateDbWithStateData() throws IOException, InterruptedException {
System.out.println("Start state DB population");
// part of the code that populates the DB and takes around 15 seconds if DB is empty
System.out.println("exit state DB population");
}
@Async
public void populateDBWithCountryData() throws IOException, InterruptedException {
System.out.println("Start country DB population");
// part of the code that populates the DB and takes around 15 seconds if DB is empty
System.out.println("exit country DB population");
}
启动日志输出:
. ____ _ __ _ _
/\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.3.RELEASE)
2020-10-03 10:29:50.222 INFO 9636 --- [ main] c.c.Covid19Trackerv2Application : Starting Covid19Trackerv2Application on DESKTOP-AE0J8AT with PID 9636 (C:\Users\bhade\Documents\covid-19-trackerv2\build\classes\java\main started by bhade in C:\Users\bhade\Documents\covid-19-trackerv2)
2020-10-03 10:29:50.224 INFO 9636 --- [ main] c.c.Covid19Trackerv2Application : No active profile set, falling back to default profiles: default
2020-10-03 10:29:50.636 INFO 9636 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data MongoDB repositories in DEFAULT mode.
2020-10-03 10:29:50.677 INFO 9636 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 37ms. Found 2 MongoDB repository interfaces.
2020-10-03 10:29:51.352 INFO 9636 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-10-03 10:29:51.361 INFO 9636 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-10-03 10:29:51.361 INFO 9636 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.37]
2020-10-03 10:29:51.429 INFO 9636 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-10-03 10:29:51.429 INFO 9636 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1164 ms
2020-10-03 10:29:51.561 INFO 9636 --- [ main] org.mongodb.driver.cluster : Cluster created with settings {hosts=[localhost:27017], mode=SINGLE, requiredClusterType=UNKNOWN, serverSelectionTimeout='30000 ms'}
2020-10-03 10:29:51.609 INFO 9636 --- [localhost:27017] org.mongodb.driver.connection : Opened connection [connectionId{localValue:1, serverValue:78}] to localhost:27017
2020-10-03 10:29:51.614 INFO 9636 --- [localhost:27017] org.mongodb.driver.cluster : Monitor thread successfully connected to server with description ServerDescription{address=localhost:27017, type=STANDALONE, state=CONNECTED, ok=true, minWireVersion=0, maxWireVersion=8, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=2658300}
2020-10-03 10:29:51.843 INFO 9636 --- [ main] o.s.s.c.ThreadPoolTaskScheduler : Initializing ExecutorService 'taskScheduler'
2020-10-03 10:29:51.847 INFO 9636 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService
2020-10-03 10:29:51.847 INFO 9636 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'taskExecutor'
Start country DB population
2020-10-03 10:29:51.894 INFO 9636 --- [ scheduling-1] org.mongodb.driver.connection : Opened connection [connectionId{localValue:2, serverValue:79}] to localhost:27017
2020-10-03 10:29:52.086 INFO 9636 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-10-03 10:29:52.095 INFO 9636 --- [ main] c.c.Covid19Trackerv2Application : Started Covid19Trackerv2Application in 2.234 seconds (JVM running for 2.525)
exit country DB population
Start state DB population
exit state DB population
日志显示第一个 @PostConstruct
被调用,但应用程序继续加载。在第一个完成之前,第二个 @PostConstruct
不会被调用。我也试过将方法调用放在相同的 @PostConstruct
中,如下所示,结果相同。
@PostConstruct
public void initializeAsyncDB() throws IOException, InterruptedException {
service.populateDBWithCountryData();
service.populateDbWithStateData();
}
有办法吗?
非常感谢任何帮助!
如何在单独的线程中启动两个填充方法?
@PostConstruct
void xx() {
new Thread(service::a).start();
new Thread(service::b).start();
}
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@SpringBootApplication
public class PostConstructApp {
public static void main(String[] args) { SpringApplication.run(PostConstructApp.class, args); }
@Component
@RequiredArgsConstructor
class C {
final MyService service;
@PostConstruct
void xx() {
new Thread(service::a).start();
new Thread(service::b).start();
}
}
@Component
class MyService {
@SneakyThrows
void a() {
System.out.println("A");
Thread.sleep(10000);
System.out.println("DONE A");
}
@SneakyThrows
void b() {
System.out.println("B");
Thread.sleep(5000);
System.out.println("DONE B");
}
}
}
日志:A和B一起打印,然后DONE B和DONE A whlie:
2020-10-03 14:07:13.640 INFO 15974 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.38]
2020-10-03 14:07:13.687 INFO 15974 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-10-03 14:07:13.687 INFO 15974 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 544 ms
A
B
2020-10-03 14:07:14.018 INFO 15974 --- [ main] s.psotcons.PostConstructApp : Started PostConstructApp in 1.046 seconds (JVM running for 1.341)
DONE B
DONE A
编辑解决方案
这最终成为我的 @Service
class 多线程工作。它同时填充数据库中的每个 table 。此解决方案不再需要 @Async
注释或任务执行器。
@Service
public class AsyncDBLoad extends Thread {
final CovidDataServices service;
Thread.UncaughtExceptionHandler exceptionHandler = new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread th, Throwable ex) {
System.out.println("Uncaught exception: " + ex.getMessage()
+ "\nIn Thread: " + th.getName());
}
};
public AsyncDBLoad(CovidDataServices service) {
this.service = service;
}
@PostConstruct
public void initializeAsyncDB() throws IOException, InterruptedException {
// Thread to populate state DB
Thread stateThread = new Thread() {
@Override
public void run() {
try {
service.populateDbWithStateData();
} catch (InterruptedException | IOException e) {
System.out.println("State thread error");
}
}
};
// Thread to populate country DB
Thread countryThread = new Thread() {
@Override
public void run() {
try {
service.populateDBWithCountryData();
} catch (InterruptedException | IOException e) {
System.out.println("Country thread error");
}
}
};
// set error handlers
stateThread.setUncaughtExceptionHandler(exceptionHandler);
countryThread.setUncaughtExceptionHandler(exceptionHandler);
// set names
stateThread.setName("State Thread");
countryThread.setName("Country Thread");
// start the threads
stateThread.start();
countryThread.start();
}
}
原问题
我正在尝试在 @Service
中使用 2 @PostConstruct
方法与 @Async
一起使用。
特别是在启动时,我正在填充数据库 table,它们彼此分开,可以同时加载。
我尝试按照 here 并异步获得第一个 @PostConstruct
到 运行 但第二个方法仍然在等待第一个完成之前开始。
这是我目前拥有的代码。
应用程序开始:
@SpringBootApplication
@EnableAsync
public class Covid19Trackerv2Application {
public static void main(String[] args) {
SpringApplication.run(Covid19Trackerv2Application.class, args);
}
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("GithubLookup-");
executor.initialize();
return executor;
}
}
@Service
调用 @Async
方法:
@Service
public class AsyncDBLoad {
final CovidDataServices service;
public AsyncDBLoad(CovidDataServices service) {
this.service = service;
}
@PostConstruct
public void initializeAsyncDB() throws IOException, InterruptedException {
service.populateDBWithCountryData();
}
@PostConstruct
public void initializeAsyncDB2() throws IOException, InterruptedException {
service.populateDbWithStateData();
}
}
持有@Async
方法的CovidDataServices@Service
的相关部分:
@Async
public void populateDbWithStateData() throws IOException, InterruptedException {
System.out.println("Start state DB population");
// part of the code that populates the DB and takes around 15 seconds if DB is empty
System.out.println("exit state DB population");
}
@Async
public void populateDBWithCountryData() throws IOException, InterruptedException {
System.out.println("Start country DB population");
// part of the code that populates the DB and takes around 15 seconds if DB is empty
System.out.println("exit country DB population");
}
启动日志输出:
. ____ _ __ _ _
/\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.3.RELEASE)
2020-10-03 10:29:50.222 INFO 9636 --- [ main] c.c.Covid19Trackerv2Application : Starting Covid19Trackerv2Application on DESKTOP-AE0J8AT with PID 9636 (C:\Users\bhade\Documents\covid-19-trackerv2\build\classes\java\main started by bhade in C:\Users\bhade\Documents\covid-19-trackerv2)
2020-10-03 10:29:50.224 INFO 9636 --- [ main] c.c.Covid19Trackerv2Application : No active profile set, falling back to default profiles: default
2020-10-03 10:29:50.636 INFO 9636 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data MongoDB repositories in DEFAULT mode.
2020-10-03 10:29:50.677 INFO 9636 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 37ms. Found 2 MongoDB repository interfaces.
2020-10-03 10:29:51.352 INFO 9636 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-10-03 10:29:51.361 INFO 9636 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-10-03 10:29:51.361 INFO 9636 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.37]
2020-10-03 10:29:51.429 INFO 9636 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-10-03 10:29:51.429 INFO 9636 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1164 ms
2020-10-03 10:29:51.561 INFO 9636 --- [ main] org.mongodb.driver.cluster : Cluster created with settings {hosts=[localhost:27017], mode=SINGLE, requiredClusterType=UNKNOWN, serverSelectionTimeout='30000 ms'}
2020-10-03 10:29:51.609 INFO 9636 --- [localhost:27017] org.mongodb.driver.connection : Opened connection [connectionId{localValue:1, serverValue:78}] to localhost:27017
2020-10-03 10:29:51.614 INFO 9636 --- [localhost:27017] org.mongodb.driver.cluster : Monitor thread successfully connected to server with description ServerDescription{address=localhost:27017, type=STANDALONE, state=CONNECTED, ok=true, minWireVersion=0, maxWireVersion=8, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=2658300}
2020-10-03 10:29:51.843 INFO 9636 --- [ main] o.s.s.c.ThreadPoolTaskScheduler : Initializing ExecutorService 'taskScheduler'
2020-10-03 10:29:51.847 INFO 9636 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService
2020-10-03 10:29:51.847 INFO 9636 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'taskExecutor'
Start country DB population
2020-10-03 10:29:51.894 INFO 9636 --- [ scheduling-1] org.mongodb.driver.connection : Opened connection [connectionId{localValue:2, serverValue:79}] to localhost:27017
2020-10-03 10:29:52.086 INFO 9636 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-10-03 10:29:52.095 INFO 9636 --- [ main] c.c.Covid19Trackerv2Application : Started Covid19Trackerv2Application in 2.234 seconds (JVM running for 2.525)
exit country DB population
Start state DB population
exit state DB population
日志显示第一个 @PostConstruct
被调用,但应用程序继续加载。在第一个完成之前,第二个 @PostConstruct
不会被调用。我也试过将方法调用放在相同的 @PostConstruct
中,如下所示,结果相同。
@PostConstruct
public void initializeAsyncDB() throws IOException, InterruptedException {
service.populateDBWithCountryData();
service.populateDbWithStateData();
}
有办法吗?
非常感谢任何帮助!
如何在单独的线程中启动两个填充方法?
@PostConstruct
void xx() {
new Thread(service::a).start();
new Thread(service::b).start();
}
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@SpringBootApplication
public class PostConstructApp {
public static void main(String[] args) { SpringApplication.run(PostConstructApp.class, args); }
@Component
@RequiredArgsConstructor
class C {
final MyService service;
@PostConstruct
void xx() {
new Thread(service::a).start();
new Thread(service::b).start();
}
}
@Component
class MyService {
@SneakyThrows
void a() {
System.out.println("A");
Thread.sleep(10000);
System.out.println("DONE A");
}
@SneakyThrows
void b() {
System.out.println("B");
Thread.sleep(5000);
System.out.println("DONE B");
}
}
}
日志:A和B一起打印,然后DONE B和DONE A whlie:
2020-10-03 14:07:13.640 INFO 15974 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.38]
2020-10-03 14:07:13.687 INFO 15974 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-10-03 14:07:13.687 INFO 15974 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 544 ms
A
B
2020-10-03 14:07:14.018 INFO 15974 --- [ main] s.psotcons.PostConstructApp : Started PostConstructApp in 1.046 seconds (JVM running for 1.341)
DONE B
DONE A