Java @Async在Spring API rest中不工作,带有接口

Java @Async在Spring API rest中不工作,带有接口,java,multithreading,asynchronous,Java,Multithreading,Asynchronous,我正在使用@Async在hibernate数据库中并行存储一些数据。我需要这样做,因为在将信息保存到数据库之前,我需要运行一些需要几分钟的任务。所以我实现了@Async 问题是@Async似乎不起作用。请查找以下代码: 网络配置 @Configuration @EnableAsync @EnableWebMvc public class WebConfig extends WebMvcConfigurerAdapter { } StudentServiceImpl: @Autowired R

我正在使用@Async在hibernate数据库中并行存储一些数据。我需要这样做,因为在将信息保存到数据库之前,我需要运行一些需要几分钟的任务。所以我实现了@Async

问题是@Async似乎不起作用。请查找以下代码:

网络配置

@Configuration
@EnableAsync
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

}
StudentServiceImpl:

@Autowired
RunSomeTaskService runSomeTaskService;

@Override
Transactional
public Response saveWithoutWaiting(StudentBO[] students, String username) throws Exception {
...
for (StudentBO student : students) {
    ....
    Future<Response> response = runSomeTaskService.doTasks(student);
    //Finish without waiting for doTasks().
}

@Override
    Transactional
    public Response saveWithWaiting(StudentBO[] students, String username) throws Exception {
    ...
    for (StudentBO student : students) {
        ....
        Future<Response> response = runSomeTaskService.doTasks(student);
        //Finish and wait for doTasks().
        response.get();
    }
public class RunSomeTaskServiceImpl extends CommonService implements RunSomeTaskService{

Student student;
@Override
    public Future<Response> doTasks(Student student) {
          Response response = new Response();
          this.student = student;
          //do Task
          return new AsyncResult<Response>(response);
       }
}
只需使用:

servlet.setAsyncSupported(true);
比如说

public class WebAppInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        ctx.register(WebConfig.class);
        ctx.setServletContext(servletContext);
        ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher",
            new DispatcherServlet(ctx));
        servlet.setLoadOnStartup(1);
        servlet.addMapping("/");
        servlet.setAsyncSupported(true); //Servlets were marked as supporting async
        // For CORS Pre Filght Request
        servlet.setInitParameter("dispatchOptionsRequest", "true");
    }
}

好了,我终于成功了

我使用遗嘱执行人的方式如下:

ExecutorService executor = Executors.newFixedThreadPool(students.size());
for (StudentBO student : students) {
    executor.submit(() -> extractDataService.doTask(student));
}

其中doTask是一个常规函数,当我不需要它在另一个线程中工作时,我就按原样调用它。当我需要线程时,我会使用上面的代码。

WebConfig.java中的@EnableAsync注释可能永远不会被扫描。xml指向spring-context.xml

您可以将web.xml中的DispatcherServlet定义更改为:

<servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </init-param>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            com.yourpackage.WebConfig
        </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <async-supported>true</async-supported>
</servlet>
在异步方法中使用如下bean名称

@Async("asyncTaskExecutor")
public Future<Response> doTasks(Student student);
@Async(“asyncTaskExecutor”)
公共未来任务(学生);

这将确保所有任务都在此线程池中执行

更复杂的方法是实现AsyncConfigurer并将AsyncExecutor设置为threadPoolTaskExecutor

下面是示例代码

@Configuration
@EnableAsync(proxyTargetClass=true) //detects @Async annotation
public class AsyncConfig implements AsyncConfigurer {

 public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10); // create 10 Threads at the time of initialization
        executor.setQueueCapacity(10); // queue capacity
        executor.setMaxPoolSize(25); // if queue is full, then it will create new thread and go till 25
        executor.setThreadNamePrefix("DEMO-");
        executor.initialize();//Set up the ExecutorService.
        return executor;
    }

    @Override
    public Executor getAsyncExecutor() {
        return threadPoolTaskExecutor();
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new YOUR_CUSTOM_EXCEPTION_HANDLER();
    }

} 

上述配置将检测@Async annotation(无论在何处提及)

您可以在将来完成,这样您就知道所有任务何时完成了

List<CompletableFuture<T>> futureList = new ArrayList<>();

for(Student student:studentList){

CompletableFuture<T> returnedFuture = CompletableFuture.supplyAsync(() -> doSomething(student),executor).exceptionally(e -> {
        log.error("Error occured in print something future",e);
        return 0;
    });

futureList.add(returnedFuture);
}

Completable.allOf(futureList);
List futureList=new ArrayList();
用于(学生:学生名单){
CompletableFuture returnedFuture=CompletableFuture.supplyAsync(()->doSomething(学生),执行者)。例外情况下(e->{
log.error(“打印时出错”,e);
返回0;
});
futureList.add(返回的Future);
}
可完成。allOf(未来列表);
然后,您可以使用“编写”或“应用”(以获取使用者)管道来完全控制任务管道。安全完成后,您可以关闭执行器



这一行是什么
future.get()
?@ErvinSzilagyi据我所知是强制等待任务完成。。。但是我只是在saveWithWaiting方法中使用它。是的,但是您的代码中没有声明
future
,忽略后面的语法错误。请在
dotask
saveWithWaiting
方法中添加带有线程名称的日志,以检查它们是否在同一线程中调用。您确定启动应用程序时会扫描
WebConfig
吗?
@Async
实际上运行得很好。问题是您没有指定特定的
TaskExecutor
来启动线程。异步请求处理(
true
)也是异步执行方法的过程。那些是不同的动物。您的
RunSomeTaskServiceImpl
的实现也有缺陷,因为它不是线程安全的。
DispatcherServlet
旁边的另一个问题是,您是否有正在加载服务的
ContextLoaderListener
?因为这会使您的
@EnableAsync
变得毫无用处,因为它生活在不同的上下文中。我在WebConfig.java中实现了这一点,并且我得到了一个错误,说我有两个ApplicationContext。。因此,我发现在web.xml中,我必须添加true,以便执行与您建议相同的配置,但它也不起作用。。。我在帖子中添加了web.xml的定义,因此您可以检查这是我得到的错误java.lang.IllegalStateException:未找到唯一的WebApplicationContext:多个DispatcherServlet注册为publishContext=true?这只是一种解决方法。当然,使用您自己的执行器就可以完成这项工作。使用它,您不需要
@Async
。创建线程池时,代码是危险的,因为您从未清理过线程池。最终您将耗尽资源和大量空闲线程。如果需要,必须在
finally
块中
关闭
执行器。另外,如果您的系统中只有16个内核可以执行此工作,您希望限制线程的数量,并且不希望创建100个线程。@M.Deinum,并且最终将在所有线程完成后触发?或者,我如何确保在触发finally块之前完成这些操作?
关闭操作将在没有更多任务要处理时立即关闭。我添加了spring-config.xml。您能看一下并分享您的意见吗?@Faabass是com.app.controller包中的WebConfig.java还是它的子包?如果不是,则不会扫描它,并且需要将其包添加到基本包中。基本包属性可以有多个逗号分隔的包,如
Base package=“x.y.z.service,x.y.z.controller”
。谢谢!我将其添加到基本包中,现在它在@Async中工作,无论如何I0m还有其他问题。请看我的帖子,我强烈建议你提出一个新问题,并将此标记为原始问题的答案。完成!这就是问题所在。。。我在WebConfig之外的同一个包中创建了一个新类,并将该包添加到基本包中,但数据库会话仍不断出错。。。我知道会话是重复的,诸如此类的事情……你能分享堆栈跟踪吗?谢谢,当然!补充。。我对此感到绝望。我正在做的是,我正在做一些处理,然后我正在以并行方式更新数据库中的学生,这使应用程序以异步方式执行方法!我更新了代码以使用CompletableFuture。。但是我在hibernate会话中遇到了错误,我在帖子中添加了stracktrace
public class WebAppInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        ctx.register(WebConfig.class);
        ctx.setServletContext(servletContext);
        ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher",
            new DispatcherServlet(ctx));
        servlet.setLoadOnStartup(1);
        servlet.addMapping("/");
        servlet.setAsyncSupported(true); //Servlets were marked as supporting async
        // For CORS Pre Filght Request
        servlet.setInitParameter("dispatchOptionsRequest", "true");
    }
}
ExecutorService executor = Executors.newFixedThreadPool(students.size());
for (StudentBO student : students) {
    executor.submit(() -> extractDataService.doTask(student));
}
<servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </init-param>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            com.yourpackage.WebConfig
        </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <async-supported>true</async-supported>
</servlet>
@Bean
public Executor asyncTaskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(5);
    executor.setMaxPoolSize(10);
    executor.setThreadNamePrefix("asynctaskpool-");
    executor.initialize();
    return executor;
} 
@Async("asyncTaskExecutor")
public Future<Response> doTasks(Student student);
@Configuration
@EnableAsync(proxyTargetClass=true) //detects @Async annotation
public class AsyncConfig implements AsyncConfigurer {

 public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10); // create 10 Threads at the time of initialization
        executor.setQueueCapacity(10); // queue capacity
        executor.setMaxPoolSize(25); // if queue is full, then it will create new thread and go till 25
        executor.setThreadNamePrefix("DEMO-");
        executor.initialize();//Set up the ExecutorService.
        return executor;
    }

    @Override
    public Executor getAsyncExecutor() {
        return threadPoolTaskExecutor();
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new YOUR_CUSTOM_EXCEPTION_HANDLER();
    }

} 
List<CompletableFuture<T>> futureList = new ArrayList<>();

for(Student student:studentList){

CompletableFuture<T> returnedFuture = CompletableFuture.supplyAsync(() -> doSomething(student),executor).exceptionally(e -> {
        log.error("Error occured in print something future",e);
        return 0;
    });

futureList.add(returnedFuture);
}

Completable.allOf(futureList);