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